Skip to content
forma.
v3.0
☰
Browse documentation
Start here
Build interfaces
Native rendering
Tooling
Security and operations
Architecture
Application guides
API reference

Docs/Application guides

FORMA 3.0

Style an application

Use ordinary CSS, build-time utilities and accessible motion with Forma.

View source ↗Markdown ↓Site guide · Forma 3.0
On this page

Forma renders ordinary DOM. Use ordinary CSS for the first component, then add a build-time styling tool if it solves a real need. This site uses authored CSS, shared design tokens and a local font; it has no styling runtime.

Give a component a stable class#

This is a component excerpt. Keep your application's existing mount and CSS loading code, or start with the complete counter example.

tsx
import { h, createSignal } from "@getforma/core";

function Counter() {
  const [count, setCount] = createSignal(0);
  return (
    <section class="counter" aria-label="Counter">
      <output aria-live="polite">{() => count()}</output>
      <button type="button" onClick={() => setCount(count() + 1)}>
        Add one
      </button>
    </section>
  );
}

The accessor () => count() keeps the output reactive instead of inserting a one-time number. Clicking changes page-local state, not a saved record. CSS changes appearance without changing that ownership. Browser examples can use derived accessors; native pages must also satisfy the compiler's admitted expression subset.

Keep responsive rules in CSS#

css
:root {
  --surface: #fbfaf6;
  --ink: #20231f;
  --accent: #d5f75b;
}
.counter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  padding: clamp(1rem, 4vw, 2rem);
  color: var(--ink);
  background: var(--surface);
}
.counter button {
  min-height: 2.75rem;
  padding: 0.6rem 1rem;
  border: 1px solid currentColor;
  border-radius: 0.5rem;
  color: var(--ink);
  background: var(--accent);
  transition: transform 160ms ease;
}
.counter button:focus-visible {
  outline: 3px solid #365ca8;
  outline-offset: 3px;
}
.counter button:active {
  transform: translateY(1px);
}
@media (prefers-reduced-motion: reduce) {
  .counter button {
    transition: none;
  }
}

The tokens control the palette; wrapping keeps the button usable on narrow screens. The focus outline makes keyboard location visible, and the pressed animation gives feedback without being the only indication that the count changed.

Use real buttons and labels. Check narrow widths, keyboard focus, zoom, dark mode and reduced motion; an animation is not an alternative to an understandable state. Avoid making essential information appear only on hover.

Include the stylesheet in the build#

js
cssEntries: [{ input: "src/styles/app.css", outfile: "app.css" }],
routes: { "/": { js: ["home"], css: ["app"] } },

These are fields of the build configuration, not a complete script; the input path matches the minimal scaffold. Put the rules in that stylesheet and rebuild. The output basename app is the route's CSS owner. The build writes its hashed asset name and integrity into the manifest. Serve that matching manifest instead of hardcoding the hashed filename into a page.

Multiple ordinary CSS inputs can be concatenated in a defined order. Keep global resets small; use component class prefixes or a naming convention to avoid accidental cross-page coupling. Forma does not automatically scope every class.

Tailwind and Sass are build choices#

The build exposes tailwind: true on a CSS entry and invokes @tailwindcss/cli. Install the CLI in the application and follow its pinned version's setup. This is a build integration, not a Forma component library. See the build configuration.

Sass can produce an ordinary CSS file before the Forma build consumes it. Pin the preprocessor, run it in the application's build script and include its output as a CSS entry. The site's current verification covers authored CSS; it does not claim a tested Sass or Tailwind version matrix. The integrations directory distinguishes tested recipes from available hooks.

Respect native response policy#

Prefer declared external stylesheets and locally hosted fonts. Native responses carry a Content Security Policy; inserting arbitrary style or script tags is not a substitute for configuring assets and policy. Return the full response so the nonce and security headers remain paired with its HTML. See response policy and the native lab.

Find your next answer.

Search Forma 3.0 documentation, guides and examples.