# Style an application

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](/play/?example=counter).

```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](/docs/3.0/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](/integrations/) 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](/docs/3.0/trusted-types/) and the [native lab](/lab/native/).
