Dashforge-UI
tailwind
DocsStarter Kits
New0.2.0-beta · Foundation primitives shipped

Tailwind, reinvented.Components, not utilities.

You're already shipping with Tailwind. Stop hand-rolling the same buttons, inputs, and modals every project. Dashforge gives you 24 typed components and 8 layout primitives that emit Tailwind utility classes you can still override. Same Tailwind you write today — half the markup, RHF-wired, RBAC-aware.

Works with:
React 19React Hook FormTailwind v3TypeScript
React, refound

The composition layer Tailwind never shipped.

Atomic Design isn't a metaphor here — it's the package boundary. Tokens are the atoms (one source of truth), Foundation primitives are the molecules, composed forms and shells are the organisms. Build pages by composing layers, not by re-sprinkling utility classes.

Foundation primitives (the molecules)

All eight shipped in 0.2.0-beta. Browse the catalogue from the sidebar.

Component composition

Style-first, or props-first?

Same WorkspaceSettings card. Same pixels in light and dark. Tailwind composes the visual; Dashforge composes the API — including the state. Pick what you'd rather write at 3 AM, and what you'd rather grep at noon.

51

LoC, utilities

19

LoC, props

−63%

Markup removed

  • Toggle state lives inside the component
  • Hover · focus · disabled · dark mode pre-wired
  • A11y (role · aria-checked · keyboard) baked in
  • Variants via props, not utility lookup
ViewingProps-first·Switch · Checkbox · Button compose the APIWorkspaceSettings.tsx
import { Switch, Checkbox, Button } from '@dashforge/tw';

export function WorkspaceSettings() {
  return (
    <div className="rounded-xl border border-neutral-200 bg-white p-6 shadow-sm dark:border-neutral-700 dark:bg-neutral-900">
      <h3 className="text-lg font-semibold">Workspace settings</h3>
      <p className="mb-5 text-sm text-neutral-600">Control access and notifications.</p>

      <Switch label="Public access" helper="Anyone with the link can view." defaultChecked />
      <Switch label="Email notifications" helper="Weekly activity digest." />
      <Checkbox label="Require 2-factor authentication" />

      <div className="mt-4 flex justify-end gap-2">
        <Button variant="text">Cancel</Button>
        <Button color="primary">Save changes</Button>
      </div>
    </div>
  );
}
The form layer

Thirty-six lines of useState. Or thirteen.

Validation, touched state, error gating, submit wiring — handled. Pass the rules as a prop and move on. Same imports, same submit, same fields — only the glue is gone.

36

LoC, before

13

LoC, after

−64%

Code removed

  • React Hook Form wired through DashForm
  • Validation rules as a prop, not a callback
  • Touched + dirty + error gating, automatic
  • A11y attributes (aria-invalid, aria-describedby) for free
ViewingAfter·DashForm + typed fields, rules as a propSignIn.tsx
import { DashForm } from '@dashforge/forms';
import { TextField, Button } from '@dashforge/tw';
import { signIn } from './api';

export function SignIn() {
  return (
    <DashForm onSubmit={signIn}>
      <TextField name="email" type="email" required rules={{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }} />
      <TextField name="password" type="password" required rules={{ minLength: 8 }} />
      <Button type="submit">Sign in</Button>
    </DashForm>
  );
}
Beyond static fields

Thirty-six lines of useEffect. Or twenty-two.

Built on Dashforge's reactive engine — visibleWhen and access props handle conditional rendering and RBAC at the prop level. No scattered effects, no prop drilling, no manual permission gating in every component.

ViewingAfter·visibleWhen + access props, zero useEffectSupportForm.tsx
import { DashForm } from '@dashforge/forms';
import { RadioGroup, TextField, Button } from '@dashforge/tw';

export function SupportForm() {
  return (
    <DashForm>
      <RadioGroup name="category" options={CATEGORIES} required />
      <TextField
        name="bugDetails"
        label="Steps to reproduce"
        visibleWhen={(engine) => engine.getNode('category')?.value === 'bug'}
        required
      />
      <Button
        color="danger"
        access={{ resource: 'invoice', action: 'delete', onUnauthorized: 'hide' }}
      >
        Delete invoice
      </Button>
    </DashForm>
  );
}

visibleWhen

Reactive predicate — the field unmounts/remounts when the dependency changes. Zero useEffect.

access

One prop wires the field to your RBAC layer. Hide, disable, or read-only by role.

rules

React Hook Form validation, declarative. Pass min/max/pattern/custom validators inline.

Ship in your existing Tailwind app today.

MIT-licensed, tree-shakeable, zero lock-in. Read the installation guide and have your first form running in under five minutes.