@codenhub/styles 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +8 -7
- package/README.md +7 -6
- package/README.pt-BR.md +8 -7
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/cyber.css +1 -1
- package/dist/aesthetics/glass.css +1 -1
- package/dist/aesthetics/index.css +2 -2
- package/dist/aesthetics/neobrutalism.css +1 -1
- package/dist/aesthetics/sketch.css +1 -1
- package/dist/cli.js +189 -0
- package/dist/components.css +2 -2
- package/dist/index.css +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/native.css +2 -2
- package/dist/palette.css +1 -1
- package/dist/theme.css +2 -2
- package/docs/accessibility.md +46 -27
- package/docs/changelog/0.8.0.md +70 -0
- package/docs/changelog/index.md +1 -0
- package/docs/es/accessibility.md +47 -28
- package/docs/es/index.md +3 -2
- package/docs/es/integrating/index.md +38 -10
- package/docs/es/integrating/tailwind.md +78 -8
- package/docs/es/integrating/without-a-bundler.md +128 -5
- package/docs/es/setup.md +42 -34
- package/docs/es/usage/aesthetics.md +19 -13
- package/docs/es/usage/buttons.md +6 -2
- package/docs/es/usage/composing.md +6 -2
- package/docs/es/usage/content-and-layout.md +8 -4
- package/docs/es/usage/customizing.md +19 -13
- package/docs/es/usage/feedback.md +5 -1
- package/docs/es/usage/forms.md +29 -4
- package/docs/es/usage/index.md +6 -2
- package/docs/es/usage/presets.md +92 -0
- package/docs/es/usage/solo.md +4 -2
- package/docs/es/usage/theming.md +28 -15
- package/docs/index.md +2 -1
- package/docs/integrating/index.md +38 -10
- package/docs/integrating/tailwind.md +77 -7
- package/docs/integrating/without-a-bundler.md +127 -4
- package/docs/pt-BR/accessibility.md +47 -28
- package/docs/pt-BR/index.md +3 -2
- package/docs/pt-BR/integrating/index.md +38 -10
- package/docs/pt-BR/integrating/tailwind.md +78 -8
- package/docs/pt-BR/integrating/without-a-bundler.md +128 -5
- package/docs/pt-BR/setup.md +42 -34
- package/docs/pt-BR/usage/aesthetics.md +19 -13
- package/docs/pt-BR/usage/buttons.md +6 -2
- package/docs/pt-BR/usage/composing.md +6 -2
- package/docs/pt-BR/usage/content-and-layout.md +8 -4
- package/docs/pt-BR/usage/customizing.md +19 -13
- package/docs/pt-BR/usage/feedback.md +5 -1
- package/docs/pt-BR/usage/forms.md +29 -4
- package/docs/pt-BR/usage/index.md +6 -2
- package/docs/pt-BR/usage/presets.md +92 -0
- package/docs/pt-BR/usage/solo.md +4 -2
- package/docs/pt-BR/usage/theming.md +21 -8
- package/docs/setup.md +30 -22
- package/docs/usage/aesthetics.md +18 -12
- package/docs/usage/buttons.md +5 -1
- package/docs/usage/composing.md +5 -1
- package/docs/usage/content-and-layout.md +7 -3
- package/docs/usage/customizing.md +18 -12
- package/docs/usage/feedback.md +4 -0
- package/docs/usage/forms.md +28 -3
- package/docs/usage/index.md +5 -1
- package/docs/usage/presets.md +91 -0
- package/docs/usage/solo.md +3 -1
- package/docs/usage/theming.md +20 -7
- package/llms-full.txt +592 -115
- package/package.json +113 -63
- package/{dist/tw → tw}/aesthetics/chunky-tile.css +39 -19
- package/{dist/tw → tw}/aesthetics/cyber.css +15 -24
- package/{dist/tw → tw}/aesthetics/glass.css +105 -36
- package/{dist/tw → tw}/aesthetics/neobrutalism.css +37 -20
- package/{dist/tw → tw}/aesthetics/sketch.css +152 -133
- package/{dist/tw → tw}/box.css +89 -12
- package/tw/components/button.css +11 -0
- package/tw/components/button.rules.css +187 -0
- package/tw/components/feedback.css +11 -0
- package/{dist/tw/components/feedback.css → tw/components/feedback.rules.css} +134 -30
- package/tw/components/form.css +11 -0
- package/{dist/tw/components/form.css → tw/components/form.rules.css} +524 -111
- package/tw/components/index.css +16 -0
- package/tw/components/rules.css +41 -0
- package/tw/components/surface.css +11 -0
- package/tw/components/surface.rules.css +222 -0
- package/tw/components/tooltip.css +11 -0
- package/{dist/tw/components/tooltip.css → tw/components/tooltip.rules.css} +43 -27
- package/{dist/tw → tw}/corner.css +18 -12
- package/{dist/tw → tw}/elevation.css +23 -17
- package/{dist/tw → tw}/index.css +11 -9
- package/tw/index.d.ts +1 -0
- package/tw/intent.css +312 -0
- package/tw/modifiers.css +127 -0
- package/tw/native.css +320 -0
- package/tw/presentation.css +139 -0
- package/tw/reset.css +10 -0
- package/{dist/tw/reset.css → tw/reset.rules.css} +7 -8
- package/tw/size.css +34 -0
- package/tw/surface.css +9 -0
- package/tw/surface.rules.css +212 -0
- package/{dist/tw → tw}/theme.css +92 -28
- package/tw/typography.css +8 -0
- package/{dist/tw/typography.css → tw/typography.rules.css} +0 -3
- package/{dist/tw → tw}/utilities/content.css +183 -63
- package/tw/utilities/index.css +11 -0
- package/tw/utilities/layout.css +208 -0
- package/tw/utilities/rules.css +8 -0
- package/dist/tw/components/button.css +0 -139
- package/dist/tw/components/index.css +0 -35
- package/dist/tw/components/surface.css +0 -184
- package/dist/tw/intent.css +0 -229
- package/dist/tw/native.css +0 -203
- package/dist/tw/palette.css +0 -460
- package/dist/tw/presentation.css +0 -68
- package/dist/tw/surface.css +0 -61
- package/dist/tw/utilities/index.css +0 -6
- package/dist/tw/utilities/layout.css +0 -144
- /package/{dist/tw → tw}/aesthetics/index.css +0 -0
- /package/{dist/tw → tw}/aesthetics/pixel.css +0 -0
package/docs/setup.md
CHANGED
|
@@ -43,9 +43,11 @@ The complete stylesheet includes the reset, the tokens, the layout and type help
|
|
|
43
43
|
|
|
44
44
|
Pick one:
|
|
45
45
|
|
|
46
|
-
- **Compiled CSS** (`@codenhub/styles`, shown above). Use it unless you have a reason not to. Any bundler that resolves CSS imports from packages works, such as Vite, webpack, or the Tailwind CLI.
|
|
46
|
+
- **Compiled CSS** (`@codenhub/styles`, shown above). Use it unless you have a reason not to. Any bundler that resolves CSS imports from packages works, such as Vite, webpack, or the Tailwind CLI. It ships every class, whether your pages use it or not.
|
|
47
47
|
- **Tailwind CSS v4 source** (`@codenhub/styles/tw`). Use it when your project already runs Tailwind v4. Your build compiles the package with your own utilities, so you get one stylesheet and Tailwind's `dark:` variant matches the package's theme. It needs Tailwind 4.3.2 or newer, below 5.
|
|
48
|
-
- **No bundler.** Link the compiled file from `node_modules` or a CDN with a `<link>` tag.
|
|
48
|
+
- **No bundler.** Link the compiled file from `node_modules` or a CDN with a `<link>` tag; see [Without a bundler](./integrating/without-a-bundler.md).
|
|
49
|
+
|
|
50
|
+
To ship only the classes your pages use in a project that does not run Tailwind, with a bundler or without one, the package's `build` command reads your files and writes a stylesheet with only those classes, which you import or link in place of the compiled one; see [Build only the classes you use](./integrating/without-a-bundler.md#build-only-the-classes-you-use).
|
|
49
51
|
|
|
50
52
|
Your framework may need its own wiring, such as where the global stylesheet goes in Next.js or Astro. Each of these guides covers one setup and then sends you back here:
|
|
51
53
|
|
|
@@ -70,16 +72,17 @@ The order of the two imports does not matter, because every entrypoint declares
|
|
|
70
72
|
## Requirements
|
|
71
73
|
|
|
72
74
|
- **Browsers:** Chrome 123, Safari 17.5, or Firefox 128 and newer. Colors use `light-dark()` and `color-scheme` (Firefox 120). `.input-group` reads a nested control's invalid and disabled state with `:has()` (Firefox 121). The elevation classes register a non-inheriting property with `@property` (Firefox 128); without it, an elevation class would also reach the unclassed elements inside it.
|
|
73
|
-
- **Tooling:** an `@import` of a package name needs a tool that resolves CSS imports from packages. Without one, link the compiled file by its path or from a CDN; see [Without a bundler](./integrating/without-a-bundler.md).
|
|
74
|
-
- **Tailwind:** 4.3.2 or newer, below 5, only for the `/tw` source entrypoints
|
|
75
|
+
- **Tooling:** an `@import` of a package name needs a tool that resolves CSS imports from packages through their `exports` map. Without one, link the compiled file by its path or from a CDN; see [Without a bundler](./integrating/without-a-bundler.md). Two common tools need a nudge, described in [Integrating → Bundlers that need a nudge](./integrating/index.md#bundlers-that-need-a-nudge): Parcel, and PostCSS with `postcss-import`.
|
|
76
|
+
- **Tailwind:** 4.3.2 or newer, below 5, only for the `/tw` source entrypoints and the `build` command, which also needs `@tailwindcss/cli`. Both are optional peer dependencies, so the compiled entrypoints do not install them.
|
|
75
77
|
- **Your application:** the package has no JavaScript. Semantic HTML, ARIA, keyboard behavior, focus management, validation, and announcements are yours; see [Accessibility](./accessibility.md).
|
|
76
78
|
|
|
77
79
|
## When nothing is styled
|
|
78
80
|
|
|
79
81
|
- **The import is missing or not loaded globally.** The classes do nothing until the stylesheet is on the page. Import it once in a stylesheet every page loads, not in a component that may not render.
|
|
80
|
-
- **The package name does not resolve.** A plain `<link>` or a tool without CSS package resolution cannot read `@import "@codenhub/styles"`. Link `node_modules/@codenhub/styles/dist/index.css`, or follow [Without a bundler](./integrating/without-a-bundler.md).
|
|
82
|
+
- **The package name does not resolve.** A plain `<link>` or a tool without CSS package resolution cannot read `@import "@codenhub/styles"`. Link `node_modules/@codenhub/styles/dist/index.css`, or follow [Without a bundler](./integrating/without-a-bundler.md). Parcel and `postcss-import` resolve it with a small change; see [Integrating → Bundlers that need a nudge](./integrating/index.md#bundlers-that-need-a-nudge).
|
|
81
83
|
- **A `/tw` path is loaded without Tailwind.** The `/tw` files are Tailwind source: they contain `@theme`, `@utility`, and `@apply`, which browsers do not understand. Your Tailwind build has to compile them; see [Tailwind CSS v4](./integrating/tailwind.md).
|
|
82
|
-
- **A
|
|
84
|
+
- **A focused `/tw` entrypoint is imported without Tailwind before it.** `@codenhub/styles/tw/button` and the other focused component files do not import Tailwind themselves. Imported alone, they compile without an error but produce no component rules. Put `@import "tailwindcss";` before them; see [Tailwind CSS v4 → Using a focused entrypoint](./integrating/tailwind.md#using-a-focused-entrypoint).
|
|
85
|
+
- **A class built at runtime is missing (Tailwind source only).** Tailwind generates a utility class, the package's components such as `.btn` included, only when it finds the name in your source files. A name assembled from strings, such as `` `btn ${tone}` ``, is invisible to that scan. Write full class names, or list them as described in [Tailwind CSS v4](./integrating/tailwind.md).
|
|
83
86
|
|
|
84
87
|
### Inlining critical CSS
|
|
85
88
|
|
|
@@ -89,9 +92,9 @@ That does not apply to the `/tw` files. They are Tailwind source, not finished C
|
|
|
89
92
|
|
|
90
93
|
## All entrypoints
|
|
91
94
|
|
|
92
|
-
Compiled entrypoints are finished CSS. `/tw` entrypoints publish the uncompiled source from `
|
|
95
|
+
Compiled entrypoints are finished CSS. `/tw` entrypoints publish the uncompiled source from `tw/`, beside `dist/`, which a Tailwind v4 build must process. A focused `/tw` component entrypoint includes the theme tokens, so its classes need nothing else from the package, but it needs `@import "tailwindcss";` before it; see [Tailwind CSS v4 → Using a focused entrypoint](./integrating/tailwind.md#using-a-focused-entrypoint).
|
|
93
96
|
|
|
94
|
-
Each import path resolves the same file under the `style`, `import`, and `default` export conditions. A CSS import is a side effect, so make sure your bundler does not drop it as unused.
|
|
97
|
+
Each import path resolves the same file under the `style`, `import`, and `default` export conditions. A `types` condition comes first and names an empty declaration, so TypeScript accepts a side-effect import such as `import "@codenhub/styles";` with `noUncheckedSideEffectImports`, which TypeScript 6 turns on by default. A CSS import is a side effect, so make sure your bundler does not drop it as unused.
|
|
95
98
|
|
|
96
99
|
| Import path | What it contains and what it changes |
|
|
97
100
|
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -99,7 +102,7 @@ Each import path resolves the same file under the `style`, `import`, and `defaul
|
|
|
99
102
|
| `@codenhub/styles/theme` | Compiled tokens and the intent, presentation, elevation, and corner-pattern classes. Sets root theme properties and the light and dark selectors. No reset and no components. |
|
|
100
103
|
| `@codenhub/styles/palette` | Generated `--palette-*` custom properties: every intent and presentation combination's fill, text, and border color as a flat value, plus flat `border`, `surface`, and `text` colors. For code that cannot use the package itself; see [Customizing → Generated palette](./usage/customizing.md#generated-palette). |
|
|
101
104
|
| `@codenhub/styles/components` | Compiled tokens, typography helpers, and every component class. Each component carries its own reduced-motion rule, and the closed-`<dialog>` fix comes with `.card`. No reset, no layout or content helpers, no classless mappings. |
|
|
102
|
-
| `@codenhub/styles/native` |
|
|
105
|
+
| `@codenhub/styles/native` | Compiled tokens, typography, layout and content helpers, components, and the package reset, plus [classless mappings](#classless-html) for plain HTML elements. Not Tailwind's Preflight. Applies the reset and broad element selectors. |
|
|
103
106
|
| `@codenhub/styles/aesthetics` | All six compiled aesthetics, `.neobrutalism`, `.glass`, `.pixel`, `.chunky-tile`, `.cyber`, and `.sketch`, each with its `-solo` class. No tokens or components of its own; import it in any order. |
|
|
104
107
|
| `@codenhub/styles/aesthetics/*` | One compiled aesthetic and its solo class: `/neobrutalism`, `/glass`, `/pixel`, `/chunky-tile`, `/cyber`, or `/sketch`. |
|
|
105
108
|
| `@codenhub/styles/tw` | Source version of the complete stylesheet. Imports Tailwind itself, then the theme, typography, helpers, components, and reset. Same global rules. |
|
|
@@ -111,9 +114,9 @@ Each import path resolves the same file under the `style`, `import`, and `defaul
|
|
|
111
114
|
| `@codenhub/styles/tw/feedback` | Source theme plus `.alert`, `.badge`, `.skeleton`, and `.progress`, with their animations. |
|
|
112
115
|
| `@codenhub/styles/tw/tooltip` | Source theme plus the tooltip classes. |
|
|
113
116
|
| `@codenhub/styles/tw/reset` | Source theme, typography helpers, and the global reset. Changes the root, body, and elements, and sets selection, motion, focus, scrollbar, and forced-colors rules. |
|
|
114
|
-
| `@codenhub/styles/tw/native` | Source version of `/native`: everything plus the reset and the classless mappings. The broadest global effect.
|
|
117
|
+
| `@codenhub/styles/tw/native` | Source version of `/native`: everything but Tailwind's Preflight, plus the package reset and the classless mappings. The broadest global effect. |
|
|
115
118
|
| `@codenhub/styles/tw/typography` | Source theme plus the typography helpers. Sets the theme selectors; no reset, no classless mappings. |
|
|
116
|
-
| `@codenhub/styles/tw/utilities` | Source theme and typography plus the layout and content helpers.
|
|
119
|
+
| `@codenhub/styles/tw/utilities` | Source theme and typography plus the layout and content helpers. |
|
|
117
120
|
| `@codenhub/styles/tw/aesthetics` | All six aesthetics as source. `/tw/aesthetics/neobrutalism`, `/glass`, `/pixel`, `/chunky-tile`, `/cyber`, and `/sketch` publish one each. Plain CSS with no theme or reset side effects. |
|
|
118
121
|
| `@codenhub/styles/package.json` | The package manifest, as JSON. For tools that read the installed version or export map; it is not a stylesheet. |
|
|
119
122
|
|
|
@@ -121,12 +124,12 @@ Do not import two entrypoints that overlap, such as `@codenhub/styles` and `@cod
|
|
|
121
124
|
|
|
122
125
|
## Classless HTML
|
|
123
126
|
|
|
124
|
-
`@codenhub/styles/native` styles plain HTML elements without any classes, for content you cannot add classes to, such as Markdown output or a CMS body. It
|
|
127
|
+
`@codenhub/styles/native` styles plain HTML elements without any classes, for content you cannot add classes to, such as Markdown output or a CMS body. It holds everything the complete stylesheet does except Tailwind's Preflight, the browser-default reset that `@codenhub/styles` brings with Tailwind. The package's own reset is in it: box sizing, the root background, text color and font, the margin reset on headings, paragraphs, and lists, selection colors, focus rings, and the reduced-motion rule. Then it adds these mappings:
|
|
125
128
|
|
|
126
129
|
| Element | Styled as |
|
|
127
130
|
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
128
131
|
| `h1`, `h2`, `h3`, `h4`, `h5` and `h6` | `.text-title-lg`, `.text-title`, `.text-title-sm`, `.text-label-lg`, `.text-label` |
|
|
129
|
-
| `
|
|
132
|
+
| `body` | `.text-body`, inherited by paragraphs, list items, and other text |
|
|
130
133
|
| `table` | `.data-table` |
|
|
131
134
|
| `kbd`, `blockquote`, `q`, `code`, `pre` | `.kbd`, `.quote`, `.quote-inline`, `.code`, `.pre` |
|
|
132
135
|
| `hr` | `.divider` |
|
|
@@ -134,6 +137,8 @@ Do not import two entrypoints that overlap, such as `@codenhub/styles` and `@cod
|
|
|
134
137
|
| `textarea`, `select` | `.textarea`, `.select` |
|
|
135
138
|
| `input type="checkbox"`, `input type="radio"` | `.checkbox`, `.radio` |
|
|
136
139
|
| `button`, `input type="button"`, `type="submit"`, `type="reset"` | `.btn` |
|
|
140
|
+
| `dialog` | `.card.floating`: hidden until opened, then a centered floating card |
|
|
141
|
+
| `progress` | `.progress`, filled to its `value`; with no `value` it is the indeterminate sweep |
|
|
137
142
|
|
|
138
143
|
```html
|
|
139
144
|
<article>
|
|
@@ -151,7 +156,9 @@ What it does not do:
|
|
|
151
156
|
- **Some input types are left alone:** `file`, `range`, `color`, and `hidden` keep the browser's own look.
|
|
152
157
|
- **No switch.** A checkbox with `role="switch"` is still painted as a checkbox; the mapping does not read roles. A page that needs switches is better served by the class-based stylesheet.
|
|
153
158
|
- **Links keep the text around them.** A link inside a heading stays heading-sized; a link is never styled as a button.
|
|
154
|
-
-
|
|
159
|
+
- **Body text is set once.** `p` and `li` take their type from the element they sit in, so a container that sets its own size, weight, or tracking applies it to the Markdown inside it. Controls keep their own tracking: a button, a badge, and a text field print `normal`, whatever the page or the container sets.
|
|
160
|
+
- **Native behavior stays native.** A `<dialog>` opens with `show()` or `showModal()` as usual; the mapping only paints it. A `<progress>` sweep stops under a reduced-motion preference, and in forced colors the bar uses the system colors.
|
|
161
|
+
- **`pre code` is not double-styled.** Code inside a `pre` drops the chip it would draw on its own: background, padding, border, shadow, glow, and clip.
|
|
155
162
|
|
|
156
163
|
The mappings are global: every matching element on the page is styled, including elements inside third-party widgets. They sit in the `base` [cascade layer](#cascade-layers), so any class beats them. Add an intent or presentation class to a mapped element, such as `<button class="destructive">`, and it applies as it would on `.btn`.
|
|
157
164
|
|
|
@@ -159,18 +166,19 @@ The mappings are global: every matching element on the page is styled, including
|
|
|
159
166
|
|
|
160
167
|
Every entrypoint puts its CSS into Tailwind's four cascade layers and declares their order before using one, so the result does not depend on which file loads first:
|
|
161
168
|
|
|
162
|
-
| Layer | Holds
|
|
163
|
-
| ------------ |
|
|
164
|
-
| `theme` | Tailwind's theme, the foundation tokens, the theme selectors, and `/palette`.
|
|
165
|
-
| `base` | The reset and the classless element styles.
|
|
166
|
-
| `components` | The intent
|
|
167
|
-
| `utilities` | Every component (`.btn`, `.card`, `.ipt`, and so on), next to your own Tailwind utilities.
|
|
168
|
-
| _unlayered_ | The forced-colors and reduced-motion overrides, the closed-`<dialog>` fix, and the `-solo` classes.
|
|
169
|
+
| Layer | Holds |
|
|
170
|
+
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
171
|
+
| `theme` | Tailwind's theme, the foundation tokens, the theme selectors, and `/palette`. |
|
|
172
|
+
| `base` | The reset and the classless element styles. |
|
|
173
|
+
| `components` | The aesthetic classes and the neutral intent values every component starts from. |
|
|
174
|
+
| `utilities` | Every component (`.btn`, `.card`, `.ipt`, and so on), next to your own Tailwind utilities. The intent, presentation, edge, elevation, corner, and size classes sit in `utilities.families`, a sublayer that loses to every other rule in `utilities`. |
|
|
175
|
+
| _unlayered_ | The forced-colors and reduced-motion overrides, the closed-`<dialog>` fix, and the `-solo` classes. |
|
|
169
176
|
|
|
170
177
|
What that means when you write your own CSS:
|
|
171
178
|
|
|
172
|
-
- **Your utility or unlayered CSS beats an intent, presentation, elevation, or aesthetic class** on the same element. `[--ui-fill:50%]` on a `.solid` button, `font-serif` on a `.pixel` region, or `.my-card { --ui-radius: 0 }` in your own stylesheet all apply.
|
|
179
|
+
- **Your utility or unlayered CSS beats an intent, presentation, elevation, or aesthetic class** on the same element. `[--ui-fill:50%]` on a `.solid` button, `font-serif` on a `.pixel` region, or `.my-card { --ui-radius-surface: 0 }` in your own stylesheet all apply.
|
|
173
180
|
- **A component and your utility share the `utilities` layer.** Specificity decides between them, then source order, as between any two utilities. Your unlayered CSS always beats a component.
|
|
181
|
+
- **Your utility beats a component's inner defaults.** `p-0 text-center` on a table cell, `text-sm` on a `.label`, or `px-8` on a `.btn.sm` applies, because the component writes those defaults so that a class on the element wins.
|
|
174
182
|
- **State beats your intent.** A field with `aria-invalid="true"` stays destructive even with an intent utility on it.
|
|
175
183
|
- **The `-solo` classes are unlayered on purpose**, so they beat a third-party component's layered or zero-specificity styles. A more specific unlayered rule, or an equally specific one that loads later, still beats them; see [Solo classes](./usage/solo.md).
|
|
176
184
|
|
package/docs/usage/aesthetics.md
CHANGED
|
@@ -70,8 +70,10 @@ The shadow is cast in the component's own intent, so a success button throws a g
|
|
|
70
70
|
|
|
71
71
|
- Content chips take the aesthetic too. A `.kbd` key cap's border changes from the quiet border gray to the ink, and from 1px to 2px, like every other border. A `.code` or `.pre` has no border by default, so it gets none; add `.edged` to give one an ink border.
|
|
72
72
|
- Only `.btn` and an opted-in `.card` respond to hover and press. A card opts in with `.interactive`, or with one of its halves — `.hoverable` for the hover response alone, `.pressable` for the press alone; a plain card, an alert, or a panel is a container and stays put.
|
|
73
|
-
- An alert sits on the offset shadow under this aesthetic, and under no other. An alert is otherwise flat, but here the shadow is a second outline rather than depth, and an alert without it looks unfinished next to everything else. This includes an alert inside a nested `.neobrutalism` region. `.flat` still removes it and `.floating` still doubles it.
|
|
73
|
+
- An alert sits on the offset shadow under this aesthetic, and under no other. An alert is otherwise flat, but here the shadow is a second outline rather than depth, and an alert without it looks unfinished next to everything else. This includes an alert inside a nested `.neobrutalism` region, and an alert that carries the class itself, `.alert.neobrutalism`. `.flat` still removes it and `.floating` still doubles it.
|
|
74
74
|
- The tooltip trigger takes the zero corner like everything else and draws square; `.radio` keeps its circle.
|
|
75
|
+
- Under `prefers-reduced-motion: reduce` the press still happens, but at once: the element lands in its shadow without travelling there. The press is the feedback that a click registered, so only its motion goes.
|
|
76
|
+
- It has no font of its own and sets the theme's, `--font-default`, on the region, so a neobrutalist region nested inside pixel, cyber, sketch, or chunky tile does not take their face. A font you set with `font-family` alone, on `body` or a container, does not reach inside the region; set it in `--font-default` too, as [Theming → Fonts](./theming.md#fonts) describes.
|
|
75
77
|
|
|
76
78
|
## Glass
|
|
77
79
|
|
|
@@ -90,8 +92,9 @@ The shadow is cast in the component's own intent, so a success button throws a g
|
|
|
90
92
|
- Every surface in a glass region is glass, a neutral `.card.soft`, `.panel`, and `.alert` included.
|
|
91
93
|
- The glass is the ground, not the fill. A presentation keeps its whole fill, so a `.solid` button, a checked toggle, and a `.solid` card or table are opaque; the default, `.soft`, and `.ghost` panes are the frosted ones.
|
|
92
94
|
- The blur applies to `.card`, `.panel`, `.alert`, the tooltip bubble, and a default or `.soft` `.data-table` only. Controls stay solid and sit on the glass: a blur under every control of a dense cluster costs a composited layer apiece and reads as noise. Controls still take the aesthetic's radius, border, and shadow.
|
|
93
|
-
- Under `prefers-reduced-transparency: reduce`, glass surfaces drop the blur and become opaque.
|
|
94
|
-
- Buttons and an opted-in `.card` press with the plain `scale(0.97)`; reduced motion
|
|
95
|
+
- Under `prefers-reduced-transparency: reduce`, and in forced colors, glass surfaces drop the blur and become opaque. Forced colors replace the colors but keep their transparency, so without this a pane would show the page through the system's background.
|
|
96
|
+
- Buttons and an opted-in `.card` press with the plain `scale(0.97)`; under reduced motion the press still happens, at once.
|
|
97
|
+
- It has no font of its own and sets the theme's, `--font-default`, on the region, as neobrutalism does.
|
|
95
98
|
- Text controls and toggles draw their line in the page's ink at 55% rather than the white hairline, which would erase an unchecked checkbox on a light page. It is `--ui-control-ink`; set it on the region to choose another.
|
|
96
99
|
- A table's frame and rules draw the page's ink at 20%, because the hairline vanishes on the table's plate. It is `--ui-rule-ink`.
|
|
97
100
|
- A table with a `<caption>` blurs behind the caption as well as its rows in Chromium and Safari, whose table box includes the caption, and under `.glass-liquid` the rim and sheen frame the caption too. Firefox keeps the caption off the pane. Label the table from a heading outside it with `aria-labelledby` where that matters.
|
|
@@ -110,13 +113,13 @@ The lens is drawn by an SVG filter in `backdrop-filter`, which Chromium renders
|
|
|
110
113
|
|
|
111
114
|
**Exceptions:**
|
|
112
115
|
|
|
113
|
-
- It reaches `.card`, `.panel`, `.alert`, the tooltip bubble, and a default or `.soft` `.data-table`. Controls stay solid, as they do under glass, except a button that asks: see [Liquid buttons](#liquid-buttons).
|
|
116
|
+
- It reaches `.card`, `.panel`, `.alert`, `.surface`, the tooltip bubble, a component of your own built with `@apply surface`, and a default or `.soft` `.data-table`. Controls stay solid, as they do under glass, except a button that asks: see [Liquid buttons](#liquid-buttons).
|
|
114
117
|
- It draws its blur and lens on each surface's `::before` and `::after`, so a surface inside a liquid region cannot carry a `::before` or `::after` of your own. Each surface also becomes a positioned stacking context; a surface you position yourself keeps your placement.
|
|
115
|
-
- Anything else that frosts under glass keeps liquid's light blur and its clearer ground, without the lens or the rim: a bare `<table>` under `/native
|
|
118
|
+
- Anything else that frosts under glass keeps liquid's light blur and its clearer ground, without the lens or the rim: a bare `<table>` under `/native`.
|
|
116
119
|
- The lens is 8px of erosion blurred over 10px on every surface, and 3px blurred over 3.5px on a button; its strength is fixed. A filter cannot read a custom property, so neither is a knob. The corners are, through `--glass-radius` and `--glass-radius-surface`, with larger defaults: a continuous corner reads smaller than a round one of the same radius.
|
|
117
|
-
- A region of another aesthetic nested inside a liquid region, or of plain `.glass`, keeps its own material and takes no layers.
|
|
120
|
+
- The nearest region decides. A region of another aesthetic nested inside a liquid region, or of plain `.glass`, keeps its own material and takes no layers, and a liquid region nested again inside that takes them back.
|
|
118
121
|
- Without `.glass` in scope it changes the corners and nothing else.
|
|
119
|
-
- Under `prefers-reduced-transparency: reduce
|
|
122
|
+
- Under `prefers-reduced-transparency: reduce`, and in forced colors, the blur and the lens go and the surface is opaque, as under glass.
|
|
120
123
|
- Text sits over a clearer pane than under glass. Over busy content, check that your text still reads: the ground is 30% of the page color, where glass's is 45%.
|
|
121
124
|
|
|
122
125
|
#### Liquid buttons
|
|
@@ -151,8 +154,9 @@ A button is a liquid pane when it carries `.glass-liquid` itself, inside a glass
|
|
|
151
154
|
**Exceptions:**
|
|
152
155
|
|
|
153
156
|
- Corners are one unit or nothing. Small chips are square instead of stepped: badges, key caps, inline code, checkboxes, and switches. One unit off each corner of a 24px badge would look like a bite, not a corner.
|
|
157
|
+
- Surfaces are square too: a `.card`, `.panel`, or `.alert` keeps the inset ring but takes no cut and clips nothing. A clip would cut everything inside it, so a tooltip in a card, a shadow, or a focus ring there would be trimmed.
|
|
154
158
|
- Tables, progress bars, and skeletons are square too. A table takes no cut because in Chromium and WebKit its box includes its caption, and the cut would land on the caption instead of the rows. Progress bars and skeletons take only the square corner.
|
|
155
|
-
- A `.pre` code block is stepped like a
|
|
159
|
+
- A `.pre` code block is stepped like a button. It has no border by default, so the cut shows with no outline around it.
|
|
156
160
|
- The outline is an inset ring, because a clip removes a real border — and the focus ring, for the same reason, is a second inset layer rather than an outline. Both are the element's own edge rather than a shadow, so the border answers `.edged` and `.edgeless` the way a border does: a `.edgeless` badge and a `.solid` button draw none, a `.edged` card draws one, and a field keeps one whatever a container asks for.
|
|
157
161
|
- The inset focus ring carries a 2px line of the page color inside it. The ring is the primary color, so on a `.solid.primary` button it would be drawn over its own color; the line is what shows there.
|
|
158
162
|
- In forced colors the ring and the cut both go, because the system removes every box shadow: components draw a real 2px border and square corners, and `.pixel-solo` an inward outline.
|
|
@@ -176,7 +180,7 @@ The bar is a darker shade of the element's own color rather than a shadow under
|
|
|
176
180
|
|
|
177
181
|
**Exceptions:**
|
|
178
182
|
|
|
179
|
-
- Only
|
|
183
|
+
- Only buttons and cards sit on a bar, because they are the components resting above zero elevation. A badge, chip, field, or tooltip is flat until it asks for depth with `.raised`, and `.flat` takes the bar off anything:
|
|
180
184
|
|
|
181
185
|
```html
|
|
182
186
|
<div class="card soft edgeless info flat">Promo panel, deliberately flat</div>
|
|
@@ -186,7 +190,8 @@ The bar is a darker shade of the element's own color rather than a shadow under
|
|
|
186
190
|
- Labels are drawn heavier and slightly tracked, through `--ui-label-weight` and `--ui-label-tracking`, so your own `font-*` or `tracking-*` utility still wins. It reaches `.btn` and `.badge`, and a bare `<button>` only where `/native` styles it. Casing is left alone: how a label is worded is the application's decision, so if you want uppercase buttons, write that rule in your own stylesheet.
|
|
187
191
|
- `.card.interactive` presses like a `.btn`, so a chunky answer tile drops onto its bar on click; `.card.pressable` gets the same press on its own. A plain `.card` stays put.
|
|
188
192
|
- The shipped `.primary` is near-black on a light page and near-white on a dark one, and the bar is always mixed toward black. On a light page, a primary button's bar is almost invisible against its near-black face (measured `1.04:1`). On a dark page, the near-white face against the same bar reads well (`2.48:1`). Give `.primary` a hue of its own if it is the call to action and needs the depth cue in both themes.
|
|
189
|
-
-
|
|
193
|
+
- Under `prefers-reduced-motion: reduce` the press still happens, but at once, as under neobrutalism.
|
|
194
|
+
- Depth in this aesthetic is the bar, never a blurred drop shadow. Anything that rests flat sits flush on the page.
|
|
190
195
|
- Controls draw a heavier gray than the tiles around them, `neutral-600` in light and `neutral-400` in dark, through `--ui-control-ink`, so a field reads as a field inside a tile.
|
|
191
196
|
|
|
192
197
|
## Cyber
|
|
@@ -259,11 +264,12 @@ The dashed button is `ghost edged` because a filled button has no separate line
|
|
|
259
264
|
|
|
260
265
|
**Exceptions:**
|
|
261
266
|
|
|
262
|
-
- Sketch names components to rotate their outlines, so its outlines reach only this package's components; a region of another aesthetic nested inside a sketch region keeps that aesthetic's corners.
|
|
267
|
+
- Sketch names components to rotate their outlines, so its outlines reach only this package's components; a region of another aesthetic nested inside a sketch region keeps that aesthetic's corners. The nearest region decides, so a sketch region nested again inside that one rotates.
|
|
263
268
|
- Chips, such as checkboxes, key caps, code, badges, switches, and progress bars, rotate through small uneven outlines set in `--ui-radius-tight`. Set that token on a chip to choose its shape.
|
|
264
269
|
- A `.btn.pill` takes a rounded pill bounded to its own height, so a wide one does not become an oval. Badges, progress bars, and switches take their chip outline. Radios and the tooltip icon stay circles, slightly uneven, so a radio is never mistaken for a checkbox. An explicit `--ui-radius-pill` on an element still wins.
|
|
265
270
|
- `--font-sketch` is yours to supply. The package ships no handwriting font and falls back to the platform `cursive` family.
|
|
266
|
-
- Buttons press with the plain `scale(0.97)`; reduced motion
|
|
271
|
+
- Buttons press with the plain `scale(0.97)`; under reduced motion the press still happens, at once.
|
|
272
|
+
- With [`/native`](../setup.md#classless-html), a plain `<hr>`, `<table>`, and checkbox take the dashed accents of `.divider`, `.data-table`, and `.checkbox`.
|
|
267
273
|
- Forced-colors mode draws the dashed accents solid, like every other material line.
|
|
268
274
|
|
|
269
275
|
## Solo classes
|
package/docs/usage/buttons.md
CHANGED
|
@@ -39,7 +39,11 @@ A button accepts one intent, one fill class, and one edge class, all optional. W
|
|
|
39
39
|
|
|
40
40
|
A **neutral** button never fills completely. Its color is the page's own text color, so a full fill would be a black or white block, the loudest thing on the page for the intent that means nothing in particular. A neutral `.solid` button stops at a 20% fill instead, a quiet gray a step darker than `.soft`. Every named intent fills to 100%.
|
|
41
41
|
|
|
42
|
-
On hover, a button's fill deepens by `--hover-step` (14%), so a `.ghost` button picks up a tint and a `.solid` one changes to its intent's hover color. On press it scales down slightly (`scale(0.97)`); an [aesthetic](./aesthetics.md) can replace that with its own press, and a user's reduced-motion preference
|
|
42
|
+
On hover, a button's fill deepens by `--hover-step` (14%), so a `.ghost` button picks up a tint and a `.solid` one changes to its intent's hover color. On press it scales down slightly (`scale(0.97)`); an [aesthetic](./aesthetics.md) can replace that with its own press, and under a user's reduced-motion preference it still happens, at once.
|
|
43
|
+
|
|
44
|
+
A neutral `.soft` or `.ghost` button inside a filled surface, such as a Cancel button in a `.card.solid.primary` or an `.alert.solid.destructive`, prints its label and its line in that surface's text color, so it stays readable on the fill. A text field does the same. A button with an intent keeps its own color, and so does a neutral `.solid` one, which rests on a fill of its own.
|
|
45
|
+
|
|
46
|
+
When a filled button has keyboard focus, a 2px line of the page color runs inside the focus ring, so the ring stays visible on a button filled with the same color, such as `.solid.primary`.
|
|
43
47
|
|
|
44
48
|
## Sizes and shapes
|
|
45
49
|
|
package/docs/usage/composing.md
CHANGED
|
@@ -22,6 +22,9 @@ This is a lookup page. [Building a screen](../concepts.md) introduces intent, pr
|
|
|
22
22
|
- **Aesthetics and [material tokens](./customizing.md#material-tokens) pass down** the same way.
|
|
23
23
|
- **Intent does not pass down.** `.primary` and the other intent classes color only the element they are on. A container that silently recolored everything inside it would turn a red danger-zone card's neutral Cancel button red. `.neutral` names the default; use it where an element would otherwise take an intent, such as a table row inside a `.success` table. See [Theming → Intent tokens](./theming.md#intent-tokens).
|
|
24
24
|
- **Elevation does not pass down.** `.raised` lifts only the element it is on; see [Elevation](#elevation).
|
|
25
|
+
- **Size does not pass down.** `.sm` and `.lg` resize only the button or control they are on, so a `.sm` form does not shrink the fields inside it. See [Buttons → Sizes and shapes](./buttons.md#sizes-and-shapes) and [Forms → Sizes](./forms.md#sizes).
|
|
26
|
+
|
|
27
|
+
With the Tailwind source entrypoints, every class above except the aesthetic classes is also a Tailwind utility, so variants such as `md:lg` and `dark:solid` work and `@apply btn primary soft sm` gives one class of yours the whole combination. The modifiers documented with each component, such as `.tight` or `.interactive`, are utilities too, so `@apply stack tight` works. The aesthetic classes, such as `.glass`, stay plain CSS, so `@apply glass` and `md:glass` do not work; write the class in the markup. See [Tailwind CSS v4 → Build your own components with `@apply`](../integrating/tailwind.md#build-your-own-components-with-apply), and [Presets](./presets.md) for when to reach for it.
|
|
25
28
|
|
|
26
29
|
## Component defaults
|
|
27
30
|
|
|
@@ -77,6 +80,7 @@ A neutral `.card.soft`, `.panel`, and `.alert` show no tint at rest. They use th
|
|
|
77
80
|
|
|
78
81
|
- A `.card.soft` with `.interactive` or `.hoverable` shows the tint on hover. A plain `.card.soft` and a `.panel` have no hover state.
|
|
79
82
|
- With one of the six built-in intents, all three keep the ordinary 12% tint, and a card deepens it on hover. An intent you defined yourself is treated as neutral here; see [Theming → Your own intent](./theming.md#your-own-intent).
|
|
83
|
+
- The presentation can come from the element or from a container: `<div class="soft"><div class="card">` rests like `.card.soft`, and a `.ghost` or `.solid` container gives a `.panel` or `.alert` the plate its own class would.
|
|
80
84
|
- A `.badge` keeps its 12% tint even when neutral. It has no border, so without the tint it would disappear into the text around it.
|
|
81
85
|
|
|
82
86
|
`.kbd`, `.code`, and `.pre` work differently. They rest on their intent's subtle tone, `--intent-subtle`, which is `--color-surface` for neutral, and a built-in intent adds 12% of its color over it. An intent you defined yourself shows its own `--intent-subtle` tint without that extra 12%.
|
|
@@ -138,7 +142,7 @@ Which classes change each component. "Material" is what an [aesthetic](./aesthet
|
|
|
138
142
|
| `.progress` | Track fill, border, and border width. The filled part is always the full intent color. |
|
|
139
143
|
| `.skeleton`, `.divider` | Intent only. |
|
|
140
144
|
|
|
141
|
-
State and modifier classes such as `.interactive`, `.hoverable`, `.pressable`, `.p-sm`, and `.vertical` are documented with their component. An invalid control is marked with `aria-invalid="true"`, not a class; see [Forms](./forms.md#show-a-validation-error).
|
|
145
|
+
State and modifier classes such as `.interactive`, `.hoverable`, `.pressable`, `.p-sm`, and `.vertical` are documented with their component. Each takes effect on the components that read it, and on `/tw` they are utilities, so `@apply card interactive` works. An invalid control is marked with `aria-invalid="true"`, not a class; see [Forms](./forms.md#show-a-validation-error).
|
|
142
146
|
|
|
143
147
|
## Elevation
|
|
144
148
|
|
|
@@ -45,7 +45,7 @@ A `.divider` takes an intent, which colors it, and ignores presentation classes,
|
|
|
45
45
|
|
|
46
46
|
The two largest sizes grow at Tailwind's `md` breakpoint, 48rem by default. With the Tailwind source entrypoints, your own `md` breakpoint applies. The package sets no size on unclassed text: body text and unclassed headings take the browser's size, or the surrounding text size under the reset; see [Setup → Classless HTML](../setup.md#classless-html) for the classless mapping.
|
|
47
47
|
|
|
48
|
-
`.text-body` is 0.875rem, smaller than the 1rem page text; use it for secondary or dense copy, not for the page's main text.
|
|
48
|
+
`.text-body` is 0.875rem, smaller than the 1rem page text; use it for secondary or dense copy, not for the page's main text. `/native` is the exception: it sets `.text-body` on `body`, so there it is the page text, and paragraphs and list items inherit it from whatever they sit in. See [Classless HTML](../setup.md#classless-html).
|
|
49
49
|
|
|
50
50
|
`.selection-contrast` on an element swaps the colors of selected text inside it to `--color-primary` on `--color-primary-contrast`. Use it on a `.solid.primary` area, where the default selection color would match the background.
|
|
51
51
|
|
|
@@ -88,7 +88,7 @@ A neutral `.card.soft` and a neutral `.panel` show no tint: they rest on the qui
|
|
|
88
88
|
| `.hoverable` | The hover color only, with no pointer cursor. |
|
|
89
89
|
| `.pressable` | The press and the pointer cursor only. |
|
|
90
90
|
|
|
91
|
-
On the default look, a press scales the card down slightly (`scale(0.97)`); an aesthetic can replace that with its own press, and a reduced-motion preference
|
|
91
|
+
On the default look, a press scales the card down slightly (`scale(0.97)`); an aesthetic can replace that with its own press, and under a reduced-motion preference it still happens, at once.
|
|
92
92
|
|
|
93
93
|
Use one half for a card that answers only one gesture. A card that holds its own button takes `.hoverable`, so that pressing the button does not press the whole card.
|
|
94
94
|
|
|
@@ -103,6 +103,8 @@ Use one half for a card that answers only one gesture. A card that holds its own
|
|
|
103
103
|
|
|
104
104
|
These classes only style. Put them on a real link or button and give it an accessible name; a `<div class="card interactive">` cannot be focused or used from a keyboard.
|
|
105
105
|
|
|
106
|
+
A disabled `.interactive` or `.pressable` card shows the not-allowed cursor instead of the pointer, whether it is a `<button>` with `disabled`, or carries `.disabled`, `aria-disabled="true"`, or `data-disabled`.
|
|
107
|
+
|
|
106
108
|
## Tables
|
|
107
109
|
|
|
108
110
|
The settings screen lists API keys in a table:
|
|
@@ -147,6 +149,8 @@ The settings screen lists API keys in a table:
|
|
|
147
149
|
|
|
148
150
|
A table's rows take the table's intent, so `.data-table.success` colors its header and lines green. A row with its own intent class uses that instead, as the revoked key's `<tr class="destructive">` does. `<tr class="neutral">` sets a row back to neutral inside a colored table. On a `.solid` table, a row with its own intent keeps the table's text color so it stays readable on the table's fill, and shows its intent through its row line and hover. This is the one place an intent passes down, because rows are parts of the table, not separate components.
|
|
149
151
|
|
|
152
|
+
Only the six built-in intents and `.neutral` recolor a body row. A row with an intent you defined yourself, such as `<tr class="beta">`, looks like the rest of the table: its text, line, and hover come from the table's intent. To mark such a row, write a built-in intent class on the row and retheme that intent's color family on the row, as [Theming → Your own intent](./theming.md#your-own-intent) describes, or paint the row with your own utilities.
|
|
153
|
+
|
|
150
154
|
### Table rules
|
|
151
155
|
|
|
152
156
|
A table draws lines under its header and above its footer. `.ruled` and `.ruleless` change them together with the lines between body rows:
|
|
@@ -211,4 +215,4 @@ A `.kbd`, `.code`, or `.pre` rests on its intent's subtle tone, `--color-surface
|
|
|
211
215
|
|
|
212
216
|
## Next
|
|
213
217
|
|
|
214
|
-
[Accessibility](../accessibility.md) lists what the CSS does for your users and what your application still owes them.
|
|
218
|
+
[Presets](./presets.md) shows how to set a look once instead of on every element. [Accessibility](../accessibility.md) lists what the CSS does for your users and what your application still owes them.
|
|
@@ -75,7 +75,7 @@ Shadow geometry is split into parts with no color in them, so they inherit safel
|
|
|
75
75
|
| `--ui-hover-shadow-y` | Shadow vertical offset while hovered. | `--ui-shadow-y` |
|
|
76
76
|
| `--ui-active-shadow-x` | Shadow horizontal offset while pressed. | `--ui-shadow-x` |
|
|
77
77
|
| `--ui-active-shadow-y` | Shadow vertical offset while pressed. | `--ui-shadow-y` |
|
|
78
|
-
| `--ui-active-transform` | Transform while pressed.
|
|
78
|
+
| `--ui-active-transform` | Transform while pressed. Kept under `prefers-reduced-motion`, which drops only its transition. | `scale(0.97)` |
|
|
79
79
|
| `--ui-active-translate-x` | Distance a press travels into the element's depth; `-y` beside it. Scaled like the shadow. | `0px` |
|
|
80
80
|
| `--ui-shadow-ink` | Percentage of shadow color taken from intent ink. | `0%` |
|
|
81
81
|
| `--ui-halo-blur` | Blur of a halo around the element; `--ui-halo-spread` beside it. Elevation does not scale it. | `0px` |
|
|
@@ -92,7 +92,7 @@ Shadow geometry is split into parts with no color in them, so they inherit safel
|
|
|
92
92
|
| `--ui-clip-tight` | Compact component silhouette. | `--ui-clip` |
|
|
93
93
|
| `--ui-focus-inset` | Inset focus layer width. | Unset |
|
|
94
94
|
| `--ui-label-weight` | Label weight of `.btn` (and, with `/native`, a bare `<button>`) and `.badge`. | Semibold on a button, bold on a badge |
|
|
95
|
-
| `--ui-label-tracking` | Label letter spacing. Unset, the
|
|
95
|
+
| `--ui-label-tracking` | Label letter spacing. Unset, it is `normal`, so neither the page's tracking nor a container's reaches the label. | `normal` |
|
|
96
96
|
| `--ui-label-case` | Label `text-transform`. | `none` on a button; inherited on a badge |
|
|
97
97
|
| `--ui-label-shadow` | Label `text-shadow`, written without a color so it takes the label's own. | `none` on a button; inherited on a badge |
|
|
98
98
|
|
|
@@ -119,7 +119,7 @@ Which corners take the corner is its own choice. `.cut-diagonal` keeps the top-l
|
|
|
119
119
|
|
|
120
120
|
`--ui-surface-image` lays any `background-image` over a surface's plate: cards, panels, alerts, and tooltips, not controls. Whoever sets it owns the contrast of the text over it, and your own `background-image` on a surface still wins.
|
|
121
121
|
|
|
122
|
-
`--ui-label-*` treats the label of a button or a badge: weight, tracking, case, and a text shadow. Write the shadow without a color, such as `0 0 6px`, so it takes the label's own color and cannot lower its contrast. Your own `font-*`, `tracking-*`, and case utilities still win.
|
|
122
|
+
`--ui-label-*` treats the label of a button or a badge: weight, tracking, case, and a text shadow. Write the shadow without a color, such as `0 0 6px`, so it takes the label's own color and cannot lower its contrast. Your own `font-*`, `tracking-*`, and case utilities still win. A label does not inherit tracking, so a `tracking-*` utility on a container leaves its buttons and badges at `normal`; to track every label in a region, set `--ui-label-tracking` on it, which they inherit.
|
|
123
123
|
|
|
124
124
|
Under forced colors each of these draws its plain version: a solid line, no halo, no painted layer, and no label shadow. The silhouette and the ring below go too. Forced colors remove every box shadow, so `--ui-clip`, `--ui-clip-tight`, `--ui-border-max`, and `--ui-shadow-edge` are cleared and a component draws its real border at `--ui-border-width`.
|
|
125
125
|
|
|
@@ -197,7 +197,7 @@ Every aesthetic's knobs reach the same way: each knob is _read_ with its default
|
|
|
197
197
|
|
|
198
198
|
Knobs and material tokens reach in opposite directions. A knob is read once, at the element with the aesthetic class, and the result passes down. So set a knob on that element or on one above it; set on an element inside the aesthetic, it changes nothing, because the value has already been read.
|
|
199
199
|
|
|
200
|
-
`--ui-radius
|
|
200
|
+
`--ui-radius`, `--ui-radius-surface`, and the other material tokens are read by each component as it draws. They work on the element they are set on and everything inside it, which is how you round one card differently from its neighbors without touching the knob. Mind which one a component reads: a surface (`.card`, `.panel`, `.alert`) takes `--ui-radius-surface`, and a control such as `.btn` or `.ipt` takes `--ui-radius`. `style="--ui-radius-surface: 0"` squares one card; `--ui-radius` on that card squares the buttons inside it and leaves the card round.
|
|
201
201
|
|
|
202
202
|
Shipped aesthetics set the shared material tokens above. Every shipped aesthetic sets `--ui-ink` for its neutral line. Every shipped aesthetic also sets `--ui-control-ink`: `.glass` and `.chunky-tile` to a line of their own, heavier than their surfaces', and the others to `--color-control-border`, since their ink is near-black or near-white and a resting checkbox outlined in it would look checked. `.glass` sets `--ui-rule-ink` too, so a table's lines read on its plate; the others clear it. Intent classes still override the intent slots on each component.
|
|
203
203
|
|
|
@@ -215,9 +215,9 @@ The same knobs tune each aesthetic's [solo class](./solo.md), which reads them o
|
|
|
215
215
|
|
|
216
216
|
```css
|
|
217
217
|
.my-badge {
|
|
218
|
-
background: var(--palette-success-soft-
|
|
218
|
+
background: var(--palette-success-soft-bg);
|
|
219
219
|
color: var(--palette-success-soft-fg);
|
|
220
|
-
border-color: var(--palette-success-soft-
|
|
220
|
+
border-color: var(--palette-success-soft-edge);
|
|
221
221
|
}
|
|
222
222
|
```
|
|
223
223
|
|
|
@@ -226,10 +226,13 @@ Every value is a finished color, never a reference to another variable, so the f
|
|
|
226
226
|
Naming: `--palette-<intent>-<presentation>-<slot>`, where `<intent>` is one of the seven published intents (`neutral`, `primary`, `secondary`, `success`, `warning`, `destructive`, `info`) and `<presentation>` is `solid`, `soft`, or `ghost`. `<slot>` is `bg`, `fg`, or `edge`. Two qualifiers layer on only where they apply:
|
|
227
227
|
|
|
228
228
|
- A ground qualifier, `page` or `subtle`, before the slot. It appears only on the `bg` and `edge` of `.soft` and `.ghost`, because a `.solid` fill covers its ground and the text color never depends on it.
|
|
229
|
-
- No qualifier is
|
|
230
|
-
- `page` is
|
|
231
|
-
- `subtle` is
|
|
232
|
-
|
|
229
|
+
- No qualifier is what `.badge` and `.btn` draw, on a transparent ground: `--palette-success-soft-bg`.
|
|
230
|
+
- `page` is what `.alert` draws, on the page: `--palette-success-soft-page-bg`.
|
|
231
|
+
- `subtle` is what `.kbd`, `.code`, and `.pre` draw, on the intent's own subtle tone: `--palette-success-soft-subtle-bg`.
|
|
232
|
+
|
|
233
|
+
Each value is read from the component itself, so it includes what the component does on its own. A neutral `.alert`, `.kbd`, `.code`, or `.pre` keeps a quieter fill than a neutral `.badge`, and its `neutral-soft-page` and `neutral-soft-subtle` values say so. `.tooltip-bubble` matches none of the three: with no intent it fills with its own tooltip tone, and its edge is solid rather than faded.
|
|
234
|
+
|
|
235
|
+
- A `-hover` suffix, on `bg` and `edge` only, because the text color does not change on hover: `--palette-success-soft-page-bg-hover`. `.alert`, `.kbd`, `.code`, and `.pre` have no hover of their own, so their hover values are what the component draws when you add `box-hover` to it.
|
|
233
236
|
|
|
234
237
|
Light values are set on `:root`. Dark values apply under `.dark`, `.theme-dark`, or `[data-theme="dark"]` on the element or an element above it, the same selectors the package's theme uses. The light values also apply under `.light`, `.theme-light`, and `[data-theme="light"]`, so the nearest explicit theme wins: a light area inside a dark one reads the light values.
|
|
235
238
|
|
|
@@ -263,7 +266,7 @@ The examples in context are in [Feedback → Show progress](./feedback.md#show-p
|
|
|
263
266
|
|
|
264
267
|
## Composition helpers
|
|
265
268
|
|
|
266
|
-
To build a component the package does not ship, such as a segmented control or a filter chip, compose it from the same classes the package's components are built from. With compiled CSS, write them as classes; with Tailwind source, you can also `@apply` them.
|
|
269
|
+
To build a component the package does not ship, such as a segmented control or a filter chip, compose it from the same classes the package's components are built from. With compiled CSS, write them as classes; with Tailwind source, you can also `@apply` them, together with the intent, presentation, elevation, and corner classes, which are utilities on `/tw` too: `@apply box box-hover neutral soft edgeless`. To give a shipped component a fixed recipe instead, such as a small soft primary button, see [Tailwind CSS v4 → Build your own components with `@apply`](../integrating/tailwind.md#build-your-own-components-with-apply).
|
|
267
270
|
|
|
268
271
|
| Class | What it gives an element |
|
|
269
272
|
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
@@ -286,7 +289,10 @@ Filter chips for the members list, one pressed and one not:
|
|
|
286
289
|
What to give each one:
|
|
287
290
|
|
|
288
291
|
- **`box` needs a fill and an edge class.** It has no defaults of its own, so with no `.solid`, `.soft`, or `.ghost` and no `.edged` or `.edgeless`, on the element or a container, it draws nothing. `surface` and `text-control` have defaults and work alone.
|
|
289
|
-
- **Name the intent
|
|
292
|
+
- **Name the intent on `box`.** A `box` element takes the intent of the nearest element around it that has one, unlike the package's components. Put `.neutral` or an intent class on it. `surface` and `text-control` are neutral unless the element has an intent class, as components are.
|
|
293
|
+
- **`@apply` carries the whole helper.** `@utility my-field { @apply text-control; }` gets what `class="text-control"` gets: the neutral intent and the field border, `.soft`, `.solid`, and `.edgeless` on the element, and the invalid look for `aria-invalid="true"`. `@apply surface` is neutral the same way. An intent class you defined yourself must be plain CSS or a utility to color such an element, not a rule in `@layer components`.
|
|
294
|
+
- **Write the class that applies a helper as a utility.** What `@apply` copies lands in the [cascade layer](../setup.md#cascade-layers) of the rule it is written in. The helpers are utilities, and `@utility my-field` keeps your class in `utilities` beside them, so your other utilities still reach it. Written as plain CSS, `.my-field { @apply text-control; }` is unlayered, and everything the helper declares beats every utility on the element: an intent class you wrote as a utility leaves the field neutral, and `mt-8` does not move a dialog. Your own unlayered CSS still contests it, by specificity and then source order, as any two unlayered rules do.
|
|
295
|
+
- **A dialog works with `@apply surface`.** A closed `<dialog>` with your own `@apply surface` class stays hidden, and an open one is centered, as with `.card`. Hiding a closed one wins over a display utility such as `flex`; a margin you set still places an open one, and a margin utility does when your class is a utility too.
|
|
290
296
|
- **Size and spacing are yours.** None of the helpers sets padding, width, or height; `text-control` does not set the 2.5rem height that `.ipt` adds.
|
|
291
297
|
- **So are behavior and semantics.** Use a real `<button>`, `<input>`, or `<a href>`, or add the role and keyboard handling yourself, and set states such as `aria-pressed` or `aria-invalid`. `box` draws the disabled look for `disabled`, `.disabled`, `aria-disabled="true"`, and `data-disabled`, but only the `disabled` attribute stops input.
|
|
292
298
|
|
package/docs/usage/feedback.md
CHANGED
|
@@ -79,6 +79,8 @@ A badge is a small pill for a status or a count:
|
|
|
79
79
|
|
|
80
80
|
A badge is soft with no border by default, and takes the same intent and presentation classes as a button: `.badge.warning` for "Payment due", `.badge.info.solid` for a filled "New". Unlike an alert, a badge with no intent keeps its light tint, because it has no border, and without the tint it would disappear into the text around it.
|
|
81
81
|
|
|
82
|
+
A neutral badge that is not `.solid`, such as a count inside a `.btn.primary`, prints in the text color of the filled component it sits in, so it stays readable on that fill. A badge with an intent keeps its own color.
|
|
83
|
+
|
|
82
84
|
## Show a placeholder while loading
|
|
83
85
|
|
|
84
86
|
While the profile card waits for data, show gray blocks where the text will be:
|
|
@@ -133,6 +135,8 @@ When you do not know how far along the work is, such as while the export is bein
|
|
|
133
135
|
|
|
134
136
|
`.active` adds a shimmer over the track of a determinate bar, to show that work is still happening between updates. Both animations stop when the user asks for reduced motion; an indeterminate bar then rests with its fill at the start.
|
|
135
137
|
|
|
138
|
+
With the [classless stylesheet](../setup.md#classless-html), a plain `<progress>` element is drawn as `.progress`: filled to its `value`, or the indeterminate sweep when it has no `value`. It announces its value itself, so it needs no `aria-valuenow`.
|
|
139
|
+
|
|
136
140
|
### How progress bars look
|
|
137
141
|
|
|
138
142
|
The fill is the intent color at full strength, or `currentColor` with no intent. The track is soft and edgeless by default: a light tint with no border.
|
package/docs/usage/forms.md
CHANGED
|
@@ -27,6 +27,25 @@ A field is a label, a control, and an optional hint, stacked:
|
|
|
27
27
|
|
|
28
28
|
A text control is at least 2.5rem tall (`--control-height`), the same height as a default button, so a field and a button line up in a row. Its default look is no fill and a border. A `.textarea` is at least 7rem tall and can be resized vertically.
|
|
29
29
|
|
|
30
|
+
### Sizes
|
|
31
|
+
|
|
32
|
+
`.ipt`, `.select`, `.textarea`, and `.input-group` take the button's size classes, so a small field and a small button line up in a row:
|
|
33
|
+
|
|
34
|
+
| Class | Height | Side padding | Text |
|
|
35
|
+
| ------- | ------ | ------------ | --------- |
|
|
36
|
+
| `.sm` | 2rem | 0.625rem | 0.875rem |
|
|
37
|
+
| default | 2.5rem | 0.75rem | inherited |
|
|
38
|
+
| `.lg` | 3rem | 1rem | 1.0625rem |
|
|
39
|
+
|
|
40
|
+
The corners scale with the size, as a button's do. A `.textarea` keeps its 7rem minimum height at every size; the class changes its text, padding, and corners. Size an `.input-group` on the group, not on the control inside it: the control and its adornments take the group's text size.
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<div class="cluster tight">
|
|
44
|
+
<input class="ipt sm" aria-label="Coupon code" />
|
|
45
|
+
<button class="btn sm" type="button">Apply</button>
|
|
46
|
+
</div>
|
|
47
|
+
```
|
|
48
|
+
|
|
30
49
|
## Show a validation error
|
|
31
50
|
|
|
32
51
|
Mark the control invalid with `aria-invalid="true"` and give the error message `.hint.destructive`:
|
|
@@ -39,7 +58,13 @@ Mark the control invalid with `aria-invalid="true"` and give the error message `
|
|
|
39
58
|
</div>
|
|
40
59
|
```
|
|
41
60
|
|
|
42
|
-
An invalid control draws its border, focus ring, and typed text in the destructive color, whatever intent class it has. The text uses the readable tone of that color, `--intent-strong`, so it stays legible on the page. The package only paints the state: deciding that the value is invalid, setting and clearing `aria-invalid`, and writing the message are your code. Remove `aria-invalid` once the value is valid again; leaving it set keeps the field red.
|
|
61
|
+
An invalid control draws its border, focus ring, and typed text in the destructive color, whatever intent class it has. The text uses the readable tone of that color, `--intent-strong`, so it stays legible on the page. The package only paints the state: deciding that the value is invalid, setting and clearing `aria-invalid`, and writing the message are your code. Remove `aria-invalid` once the value is valid again; leaving it set keeps the field red. On a checkbox, radio, or switch, `aria-invalid="true"` draws the toggle exactly as `.destructive` does, checked mark included.
|
|
62
|
+
|
|
63
|
+
A hint takes any of the six built-in intents, not only `.destructive`: `.hint.success` confirms a value was accepted, and `.hint.warning` flags one that is allowed but unusual. Each prints in its intent's readable tone, `--intent-strong`, which holds 4.5:1 against the page in both themes. A hint with no intent, with `.neutral`, or with an [intent you defined yourself](./theming.md#your-own-intent), stays secondary-colored. Only a class on the hint itself colors it; an intent on the field or a container around it does not.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<input class="ipt success" id="username" value="ada" aria-describedby="username-hint" /> <span class="hint success" id="username-hint">That username is available.</span>
|
|
67
|
+
```
|
|
43
68
|
|
|
44
69
|
## Put the profile form together
|
|
45
70
|
|
|
@@ -83,7 +108,7 @@ A `.select` draws its own arrow. With `multiple` or a `size` above 1 it becomes
|
|
|
83
108
|
|
|
84
109
|
## Add an icon or a fixed prefix
|
|
85
110
|
|
|
86
|
-
`.input-group` puts an icon or a piece of text inside a field's border. The group draws the border, background, focus ring, and invalid and disabled states; the control inside it gives up its own, so the border is drawn once. Place the adornment before the control to put it at the start, after it to put it at the end.
|
|
111
|
+
`.input-group` puts an icon or a piece of text inside a field's border. The group draws the border, background, focus ring, and invalid and disabled states; the control inside it gives up its own, so the border is drawn once. Place the adornment before the control to put it at the start, after it to put it at the end. A group is the height of a lone field, so it lines up with the fields and buttons beside it.
|
|
87
112
|
|
|
88
113
|
The profile address always starts with the same host, so it is a fixed prefix:
|
|
89
114
|
|
|
@@ -115,7 +140,7 @@ A `.select` works in a group too. The group removes the control's side padding,
|
|
|
115
140
|
|
|
116
141
|
Any element works as the adornment: an icon class, an inline `<svg>`, an `<img>`, or text. The package ships no icons for fields, because a built-in image could not follow your text color, theme, or aesthetic.
|
|
117
142
|
|
|
118
|
-
The group reads intent and presentation the way a lone `.ipt` does: `.input-group.primary` colors the border, and `.input-group.soft.edgeless` is a field sunk into the page. `aria-invalid="true"` or `disabled` on the control inside applies to the whole group, and so does either on the group itself. The focus ring appears when
|
|
143
|
+
The group reads intent and presentation the way a lone `.ipt` does: `.input-group.primary` colors the border and the typed text, and `.input-group.soft.edgeless` is a field sunk into the page. Put the intent on the group, not on the control inside: the group draws the field, so the control's text takes the group's intent and an intent class on the control has no effect. `aria-invalid="true"` or `disabled` on the control inside applies to the whole group, and so does either on the group itself. The group's focus ring appears when its field has focus, so unlike a lone field it also shows after a mouse click. A button inside the group, such as a clear button, draws its own ring when it has focus, and the group draws none: one ring always marks the one control that has focus.
|
|
119
144
|
|
|
120
145
|
## Browser decorations
|
|
121
146
|
|
package/docs/usage/index.md
CHANGED
|
@@ -7,7 +7,7 @@ order: 4
|
|
|
7
7
|
|
|
8
8
|
# Usage
|
|
9
9
|
|
|
10
|
-
These guides finish the settings screen started in [Building a screen](../concepts.md). Read the first seven in order: each one adds a part of the screen and assumes the ones before it. The
|
|
10
|
+
These guides finish the settings screen started in [Building a screen](../concepts.md). Read the first seven in order: each one adds a part of the screen and assumes the ones before it. The next three are lookup pages; you will be sent to them when a guide needs the full table, and you can come back whenever you need a default or a rule. The last page, Presets, is for when the screen works and you are tired of typing the same classes.
|
|
11
11
|
|
|
12
12
|
Guides, in order:
|
|
13
13
|
|
|
@@ -25,4 +25,8 @@ Lookup pages:
|
|
|
25
25
|
- [Customizing](./customizing.md): presentation and material tokens, aesthetic knobs, the six public component variables, the generated palette, and the composition helpers for building your own components.
|
|
26
26
|
- [Content and layout](./content-and-layout.md): every layout helper, the type scale, cards and panels, tables, quotes, code, and keyboard keys.
|
|
27
27
|
|
|
28
|
+
Once the screen works:
|
|
29
|
+
|
|
30
|
+
- [Presets](./presets.md): set a look once, with axis classes on a container, theme and material tokens, or your own component classes built with `@apply`.
|
|
31
|
+
|
|
28
32
|
When you finish, [Accessibility](../accessibility.md) collects what the CSS does for you and what your application still owes its users.
|