A clear heading
Use semantic HTML for meaning and emphasis.
Good defaults should support the content.
Press Ctrl + K to search.
const greeting = "Hello";
Build shadcn/ui-inspired components with plain HTML and one stylesheet. Start with polished defaults, then make them your own with CSS variables. No Tailwind, JavaScript framework, or build step required.
Add one stylesheet to your HTML page. Buttons, form controls, typography, and tables get shadcn/ui-inspired styles by default. The core is CSS-only: no JavaScript or npm install needed.
Write semantic HTML and the stylesheet handles the look. Native elements do not need component classes. Serve your page over HTTP, including when you test locally. Keep the URL pinned to a release and change the version when you want to update.
Send a message and we will reply by email.
To add a component, copy an example below. Use named classes such as class="card" for patterns without a native HTML element, and attributes such as data-variant="outline" to choose a style. Override CSS variables such as --primary, --primary-foreground, and --radius to change colors and rounded corners. Optional JavaScript adds behavior such as syntax highlighting and code copying.
If you use a bundler, install @faith-tools/sensible-ui and import the complete stylesheet in your CSS:
Standalone component imports include the theme and base styles they need. When markup combines components, import each component. For example, <table class="card"> needs both /table and /card.
| Export | Purpose |
|---|---|
., /css | Complete bundled stylesheet |
/min | Minified complete bundle |
/index | Source CSS entry point |
/base | Theme tokens and semantic reset |
/theme | Theme tokens and cascade layer order |
/accordion, /badge, /button, /card | Standalone component modules |
/code/css | Optional code component styles; JavaScript is under /code |
/description-list, /dialog, /image, /input, /item | Standalone component modules |
/spinner, /table, /typography | Standalone component modules |
/utils | Named layouts, container, and composition helpers |
/utilities, /utilities/min | Optional generated atomic utilities |
/view-transition | Optional same-origin page transitions |
Standalone import: @import '@faith-tools/sensible-ui/typography';
Headings, paragraphs, links, lists, code, keyboard input, quotations, and inline text semantics are styled without classes. Keep the native element that matches the content’s meaning.
Geist and Geist Mono are optional. The stylesheet prefers them but does not load them, so your system fonts are used unless you provide the fonts separately. See the font setup in the README for HTML and Next.js examples, or override --font-sans and --font-mono to use your own fonts.
Use semantic HTML for meaning and emphasis.
Good defaults should support the content.
Press Ctrl + K to search.
const greeting = "Hello";
Standalone import: @import '@faith-tools/sensible-ui/button';
Native buttons, button-like inputs, and a.button are supported. Use data-variant for primary, secondary, outline, ghost, link, or destructive treatment. Use data-size for sm, lg, or icon. Native disabled, aria-pressed, aria-busy, and aria-invalid attributes style state.
Use a button for actions and a link for navigation. Give icon-only buttons an accessible name.
Standalone import: @import '@faith-tools/sensible-ui/input';
The input module styles native controls and their labels. Associate every control with a label. Use fieldset andlegend for related choices, and use aria-describedby when an error or hint needs to be announced.
Text, email, password, number, search, URL, and similar inputs share the same semantic default.
Use the native select when choosing one option from a list. Group long option lists with optgroup.
Use checkboxes for independent choices. The checked and disabled states are native.
Radio buttons with the same name represent one choice. Wrap the group in a fieldset with a legend.
Add role="switch" to a checkbox only when the control immediately turns a setting on or off.
Provide a visible label and meaningful minimum, maximum, and initial values.
Date and datetime-local inputs preserve each browser’s native picker and keyboard behavior.
Use the accept attribute as a picker hint, not as file validation.
Standalone import: @import '@faith-tools/sensible-ui/image';
Images receive responsive sizing and rounded corners. Use an empty alt value for decorative images and useful alternative text for informative images. Pair an image and caption with figure and figcaption.
Standalone import: @import '@faith-tools/sensible-ui/accordion';
Native details and summary provide disclosure behavior without JavaScript. The open attribute sets the initial state. Give related details elements the same name when only one should remain open.
No. It uses the native details and summary elements.
Yes. Give related details elements the same name.
Standalone import: @import '@faith-tools/sensible-ui/dialog';
A native dialog opens modally with showModal() or non-modally with show(). The standalone dialog import includes button styles; import /input separately for form controls. JavaScript only connects your trigger to the native method; no component runtime is required. Do not set open directly to create a modal.
Identify the dialog with aria-labelledby and add aria-describedby for a short description. Direct header, section, and footer children provide the title, scrollable content, and actions. Provide a visible close control. A form method="dialog" closes without submitting data; handle saving in your application and close only after it succeeds. Choose initial focus with autofocus when needed.
Modal dialogs close with Escape by default, but a backdrop click does not dismiss them. Add closedby="any" only when discarding the dialog is safe. Keep a visible close control for browsers that do not support closedby.
The content region scrolls while the header and actions stay visible. On short viewports, the whole dialog scrolls so the body remains readable. The same layout works in dark mode and at narrow widths.
show() leaves the page interactive and has no backdrop or automatic Escape dismissal. Always include an explicit close control. Use a modal when the task requires focus to stay inside the dialog.
Standalone import: @import '@faith-tools/sensible-ui/description-list';
Use a description list for name-value groups, terms and definitions, or metadata. Multiple terms may share a description and one term may have multiple descriptions. Add class="card" and import the card module for the bordered treatment.
Standalone import: @import '@faith-tools/sensible-ui/table';
Use tables for two-dimensional data. Add a caption when the surrounding context does not already identify the table, and usescope on row and column headers. Add class="card" and import the card module for the bordered treatment.
| Name | Role | Status |
|---|---|---|
| Margaret Nguyen | Owner | Active |
| Hoshi Nakamura | Editor | Invited |
| Total | 2 | |
Standalone import: @import '@faith-tools/sensible-ui/card';
Add class="card" to a semantic container. Direct header, section, and footer children define its regions. Add data-slot="card-action" to a header action. Cards adapt their layout through container queries.
For growing organizations
Invite collaborators and share project settings.
Standalone import: @import '@faith-tools/sensible-ui/badge';
Add class="badge" to short status or category text. Supported variants are primary, secondary, outline, and destructive. Use a link only when the badge navigates somewhere. aria-invalid="true" provides the invalid state.
Standalone import: @import '@faith-tools/sensible-ui/item';
An item is a compact content row with an optional icon or action. Use a.item when the entire row navigates. Do not put another interactive control inside a linked item.
Manage members, billing, and notifications.
The whole row is one descriptive link.
Standalone import: @import '@faith-tools/sensible-ui/spinner';
Add aria-busy="true" while an element is updating. Add data-variant="overlay" to dim existing children. Keep visible loading text or an accessible name so the state is understandable without relying on motion.
The current content remains visible while loading.
Standalone import: @import '@faith-tools/sensible-ui/utils';
Add class="container" to center content and limit its width. It fills the available width up to 640px by default, with 1rem of padding on all sides. At viewport widths of 40rem and above, the padding becomes 1.5rem. The core bundle includes this class; no companion stylesheet is needed.
Set --container-width on the container to choose a different maximum width. The example uses 32rem. The class works on main, section, or div; a plain main does not get container styles automatically. .page is an alias for .container.
Keep content readable without stretching across the whole page.
Standalone import: @import '@faith-tools/sensible-ui/utils';
The core bundle includes .stack for vertical flow, .cluster for wrapping inline groups, .split for separated content, and .auto-grid for intrinsic grids. .y-stack aliases stack and .x-stack is a non-wrapping inline stack. Set --layout-gap to adjust spacing and --min-item-size to control grid wrapping.
Add class="dark" to an ancestor to select the bundled dark theme. Components use the same markup in both themes. System preference behavior is not enabled by the core bundle.
The same semantic markup works.
Use the scoped bundle when adding Sensible UI to an existing application. Semantic defaults apply only inside a neutral .sensible-ui wrapper, leaving the rest of the page alone.
If you use a bundler, import the scoped bundle in your CSS instead of using the CDN link:
Put the class on a wrapper, not on a card, table, link, or form control. Sensible UI layout and utility classes such as .stack, .mt-4, and .size-8 also belong on descendants, not the scope root. You can combine the scope class with a host-owned wrapper class. Scoped component and utility imports use the same names under /scoped, such as /scoped/button and /scoped/utilities. Scoped mode requires browser support for @scope.
Optional import: @import '@faith-tools/sensible-ui/utilities';
The companion stylesheet provides token-backed display, flex, grid, alignment, sizing, spacing, positioning, overflow, text, aspect-ratio, and accessibility helpers. It is plain generated CSS and requires no template scanning or consumer-side tooling. Override the --space-* custom properties to change its spacing scale. Prefer named helpers such as .stack, .y-stack, and .x-stack for common composition, then use atomic helpers for exceptions. Breakpoint-prefixed variants are intentionally not generated; use intrinsic layouts or consumer-owned media queries instead.
A long status message is truncated with an ellipsis when the available inline space is limited, preserving a compact row without wrapping into the content below or pushing neighboring controls out of view.
Add the optional stylesheet and browser module to pages that need syntax highlighting. The component works without the main Sensible UI stylesheet. Serve the page over HTTP so the browser can load the module.
Add the component styles to your HTML:
Load the component as a browser module:
Add sensible-code with a read-only textarea containing the source:
If you use a bundler, install @faith-tools/sensible-ui. Import the styles in your CSS and the component in your browser JavaScript instead of using the CDN tags:
The code below shows syntax colors, a Wrap lines toggle, and a Copy button. Code scrolls horizontally by default at every screen width. Set data-wrap="true" to start a block wrapped. Readers can switch either block between wrapping and scrolling. Without JavaScript, the read-only text area remains readable. The component creates pre and code when it loads. In HTML source, escape & before entity names. If the example contains </textarea>, write its opening angle bracket as <. Set language to a supported language name, such as html, css, or python.