@codenhub/styles 0.6.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/NOTICE +18 -16
- package/README.es.md +62 -0
- package/README.md +28 -22
- package/README.pt-BR.md +62 -0
- 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/pixel.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 +108 -60
- package/docs/changelog/0.1.1.md +1 -0
- package/docs/changelog/0.2.0.md +1 -0
- package/docs/changelog/0.3.0.md +1 -0
- package/docs/changelog/0.4.0.md +1 -0
- package/docs/changelog/0.5.0.md +8 -3
- package/docs/changelog/0.6.0.md +2 -1
- package/docs/changelog/0.7.0.md +50 -0
- package/docs/changelog/0.8.0.md +70 -0
- package/docs/changelog/index.md +2 -0
- package/docs/concepts.md +188 -46
- package/docs/es/accessibility.md +169 -0
- package/docs/es/concepts.md +212 -0
- package/docs/es/index.md +105 -0
- package/docs/es/integrating/astro.md +35 -0
- package/docs/es/integrating/index.md +75 -0
- package/docs/es/integrating/nextjs.md +67 -0
- package/docs/es/integrating/svelte.md +45 -0
- package/docs/es/integrating/tailwind.md +148 -0
- package/docs/es/integrating/vue.md +31 -0
- package/docs/es/integrating/without-a-bundler.md +238 -0
- package/docs/es/setup.md +188 -0
- package/docs/es/usage/aesthetics.md +282 -0
- package/docs/es/usage/buttons.md +135 -0
- package/docs/es/usage/composing.md +168 -0
- package/docs/es/usage/content-and-layout.md +219 -0
- package/docs/es/usage/customizing.md +304 -0
- package/docs/es/usage/feedback.md +157 -0
- package/docs/es/usage/forms.md +256 -0
- package/docs/es/usage/index.md +33 -0
- package/docs/es/usage/presets.md +92 -0
- package/docs/es/usage/solo.md +86 -0
- package/docs/es/usage/theming.md +309 -0
- package/docs/es/usage/tooltips.md +127 -0
- package/docs/index.md +86 -26
- package/docs/integrating/astro.md +3 -4
- package/docs/integrating/index.md +46 -13
- package/docs/integrating/nextjs.md +3 -4
- package/docs/integrating/svelte.md +5 -4
- package/docs/integrating/tailwind.md +89 -6
- package/docs/integrating/vue.md +3 -4
- package/docs/integrating/without-a-bundler.md +237 -0
- package/docs/pt-BR/accessibility.md +169 -0
- package/docs/pt-BR/concepts.md +212 -0
- package/docs/pt-BR/index.md +105 -0
- package/docs/pt-BR/integrating/astro.md +35 -0
- package/docs/pt-BR/integrating/index.md +75 -0
- package/docs/pt-BR/integrating/nextjs.md +67 -0
- package/docs/pt-BR/integrating/svelte.md +45 -0
- package/docs/pt-BR/integrating/tailwind.md +148 -0
- package/docs/pt-BR/integrating/vue.md +31 -0
- package/docs/pt-BR/integrating/without-a-bundler.md +238 -0
- package/docs/pt-BR/setup.md +188 -0
- package/docs/pt-BR/usage/aesthetics.md +282 -0
- package/docs/pt-BR/usage/buttons.md +135 -0
- package/docs/pt-BR/usage/composing.md +168 -0
- package/docs/pt-BR/usage/content-and-layout.md +219 -0
- package/docs/pt-BR/usage/customizing.md +304 -0
- package/docs/pt-BR/usage/feedback.md +157 -0
- package/docs/pt-BR/usage/forms.md +256 -0
- package/docs/pt-BR/usage/index.md +33 -0
- package/docs/pt-BR/usage/presets.md +92 -0
- package/docs/pt-BR/usage/solo.md +86 -0
- package/docs/pt-BR/usage/theming.md +309 -0
- package/docs/pt-BR/usage/tooltips.md +127 -0
- package/docs/setup.md +142 -91
- package/docs/usage/aesthetics.md +89 -73
- package/docs/usage/buttons.md +112 -40
- package/docs/usage/composing.md +109 -92
- package/docs/usage/content-and-layout.md +160 -125
- package/docs/usage/customizing.md +117 -41
- package/docs/usage/feedback.md +138 -71
- package/docs/usage/forms.md +201 -107
- package/docs/usage/index.md +22 -14
- package/docs/usage/presets.md +91 -0
- package/docs/usage/solo.md +85 -0
- package/docs/usage/theming.md +273 -118
- package/docs/usage/tooltips.md +90 -49
- package/llms-full.txt +2820 -1298
- package/llms.txt +24 -22
- package/package.json +134 -91
- package/{dist/tw → tw}/aesthetics/chunky-tile.css +107 -29
- package/{dist/tw → tw}/aesthetics/cyber.css +88 -40
- package/{dist/tw → tw}/aesthetics/glass.css +257 -61
- package/{dist/tw → tw}/aesthetics/neobrutalism.css +113 -20
- package/{dist/tw → tw}/aesthetics/pixel.css +93 -8
- package/tw/aesthetics/sketch.css +417 -0
- package/{dist/tw → tw}/box.css +105 -9
- 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} +136 -32
- package/tw/components/form.css +11 -0
- package/{dist/tw/components/form.css → tw/components/form.rules.css} +577 -125
- 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 +12 -10
- 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} +21 -22
- 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 +121 -45
- package/tw/typography.css +8 -0
- package/{dist/tw/typography.css → tw/typography.rules.css} +0 -3
- package/{dist/tw → tw}/utilities/content.css +212 -50
- package/tw/utilities/index.css +11 -0
- package/tw/utilities/layout.css +208 -0
- package/tw/utilities/rules.css +8 -0
- package/dist/tw/aesthetics/sketch.css +0 -333
- package/dist/tw/components/button.css +0 -139
- package/dist/tw/components/index.css +0 -36
- package/dist/tw/components/loader.css +0 -116
- package/dist/tw/components/surface.css +0 -175
- package/dist/tw/intent.css +0 -228
- 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 -130
- /package/{dist/tw → tw}/aesthetics/index.css +0 -0
package/docs/usage/aesthetics.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Aesthetics
|
|
3
|
-
description: The six
|
|
4
|
-
order:
|
|
3
|
+
description: The six optional looks, what each one changes about a component, its knobs, and its limits.
|
|
4
|
+
order: 6
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Aesthetics
|
|
8
8
|
|
|
9
|
-
An aesthetic
|
|
9
|
+
The settings screen has your colors. An aesthetic changes what its components are made of: the corners, the thickness of the borders, the shadow, and the shape. Put `.neobrutalism` on the `<main>` of the settings screen and every card, button, field, and badge in it gets square corners, a thick outline in the ink color, and a hard offset shadow, while keeping its colors. This page shows how to add one, then describes each of the six: what it changes, the number you can tune, and where it behaves differently from the default look.
|
|
10
|
+
|
|
11
|
+
## Add an aesthetic
|
|
12
|
+
|
|
13
|
+
Aesthetics ship in a separate entrypoint, so load it next to the stylesheet:
|
|
10
14
|
|
|
11
15
|
```css
|
|
12
16
|
@import "@codenhub/styles";
|
|
@@ -15,52 +19,61 @@ An aesthetic decides what a component is _made of_: its radius, border thickness
|
|
|
15
19
|
|
|
16
20
|
Load order does not matter: the aesthetic classes sit in the `components` cascade layer, and every entrypoint declares the layer order before it uses one, so an aesthetic imported before the base stylesheet wins exactly as one imported after it. See [Setup → Cascade layers](../setup.md#cascade-layers).
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
Then put the class on any element. Unlike intent, an aesthetic passes down: everything inside the element takes it.
|
|
19
23
|
|
|
20
24
|
```html
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
+
<main class="neobrutalism stack">
|
|
26
|
+
<section class="card stack" aria-labelledby="profile-title">…</section>
|
|
27
|
+
<section class="card soft destructive stack" aria-labelledby="danger-title">
|
|
28
|
+
…
|
|
29
|
+
<button class="btn destructive" type="button">Delete account</button>
|
|
30
|
+
</section>
|
|
31
|
+
</main>
|
|
25
32
|
```
|
|
26
33
|
|
|
34
|
+
The danger zone keeps its red: under neobrutalism its outline and shadow are red too, because this aesthetic draws them in the component's own intent. To load one aesthetic instead of all six, import it by name, such as `@codenhub/styles/aesthetics/neobrutalism`; [Setup → All entrypoints](../setup.md#all-entrypoints) lists them.
|
|
35
|
+
|
|
27
36
|
| Class | Look |
|
|
28
37
|
| --------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
38
|
| `.neobrutalism` | No radius, a thick ink outline, a hard unblurred offset shadow, and a press that moves the element into it. |
|
|
30
39
|
| `.glass` | Translucent surfaces over a blurred backdrop with a hairline highlight edge. |
|
|
31
40
|
| `.pixel` | Corners cut by one grid unit, a chunky outline drawn as an inset ring, and the consumer-supplied `--font-pixel` over monospace. |
|
|
32
|
-
| `.chunky-tile` | Rounded
|
|
33
|
-
| `.cyber` |
|
|
41
|
+
| `.chunky-tile` | Rounded tiles seated on a darker bar of their own color, with a press that moves the element down into its own bar. |
|
|
42
|
+
| `.cyber` | Beveled corners, a thin bright edge, and a glow in the component's own color. |
|
|
34
43
|
| `.sketch` | Varying elliptical corners, a solid ink line with dashed accents, a small offset shadow, and an optional handwriting face. |
|
|
35
44
|
|
|
36
|
-
Aesthetics
|
|
45
|
+
Aesthetics work with every fill and edge combination. `.ghost.edgeless` removes the fill and the border, so on a component whose aesthetic shows only through those and its shadow, little of the aesthetic is left; that is intended.
|
|
46
|
+
|
|
47
|
+
A presentation class on the element still wins over the aesthetic's defaults. The aesthetic sets how thick the border is; presentation decides whether it is drawn.
|
|
37
48
|
|
|
38
|
-
An
|
|
49
|
+
An aesthetic class on a component wins over one inherited from a container. This includes tooltip bubbles: `.tooltip-bubble.glass` gets the complete glass bubble under a pixel ancestor, and `.tooltip-bubble.pixel` gets the complete stepped bubble under a glass ancestor.
|
|
39
50
|
|
|
40
|
-
|
|
51
|
+
Your own CSS wins over an aesthetic. The aesthetic classes sit in the `components` cascade layer, so a rule in your own unlayered stylesheet beats them, and so does a Tailwind utility on the same element, such as `font-mono` on a `.pixel` region or `[--ui-radius:0]` on a `.cyber` one. See [Setup → Cascade layers](../setup.md#cascade-layers).
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
Most aesthetics scale from one number you can set, such as the offset of the neobrutalism shadow or the size of a pixel. These are the aesthetic's knobs, and each section below names its own. [Customizing → Aesthetic tokens](./customizing.md#aesthetic-tokens) explains where they can be set.
|
|
43
54
|
|
|
44
|
-
|
|
55
|
+
To paint an aesthetic onto an element from another library, such as a toast or a dialog, use its [solo class](./solo.md).
|
|
45
56
|
|
|
46
57
|
## Neobrutalism
|
|
47
58
|
|
|
48
|
-
The shadow is cast in the component's own intent, so a success button throws a green shadow and a destructive card a red one. With no intent,
|
|
59
|
+
The shadow is cast in the component's own intent, so a success button throws a green shadow and a destructive card a red one. With no intent, the outline and the shadow are the ink: near-black on a light page and near-white on a dark one. Fields and toggles are the exception. They draw the theme's control border, as they do with no aesthetic, because a resting checkbox outlined in near-black would look as loud as a checked one. The same holds under pixel, cyber, and sketch.
|
|
49
60
|
|
|
50
61
|
`--neo-offset` is the knob: the shadow offset and the distance the press travels are the same number, so scaling the look scales both at once.
|
|
51
62
|
|
|
52
63
|
```html
|
|
53
64
|
<section class="neobrutalism" style="--neo-offset: 6px">
|
|
54
|
-
<button class="btn primary">A
|
|
65
|
+
<button class="btn primary">A deeper shadow</button>
|
|
55
66
|
</section>
|
|
56
67
|
```
|
|
57
68
|
|
|
58
69
|
**Exceptions:**
|
|
59
70
|
|
|
60
|
-
- Content chips
|
|
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.
|
|
61
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.
|
|
62
|
-
- An alert
|
|
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.
|
|
63
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.
|
|
64
77
|
|
|
65
78
|
## Glass
|
|
66
79
|
|
|
@@ -79,8 +92,9 @@ The shadow is cast in the component's own intent, so a success button throws a g
|
|
|
79
92
|
- Every surface in a glass region is glass, a neutral `.card.soft`, `.panel`, and `.alert` included.
|
|
80
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.
|
|
81
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.
|
|
82
|
-
- Under `prefers-reduced-transparency: reduce`, glass surfaces drop the blur and become opaque.
|
|
83
|
-
- 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.
|
|
84
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.
|
|
85
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`.
|
|
86
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.
|
|
@@ -99,13 +113,33 @@ The lens is drawn by an SVG filter in `backdrop-filter`, which Chromium renders
|
|
|
99
113
|
|
|
100
114
|
**Exceptions:**
|
|
101
115
|
|
|
102
|
-
- It reaches `.card`, `.panel`, `.alert`, the tooltip bubble, and a default or `.soft` `.data-table`. Controls stay solid, as they do under glass.
|
|
103
|
-
- 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
|
|
104
|
-
-
|
|
105
|
-
-
|
|
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).
|
|
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.
|
|
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`.
|
|
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.
|
|
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.
|
|
106
121
|
- Without `.glass` in scope it changes the corners and nothing else.
|
|
107
|
-
- Under `prefers-reduced-transparency: reduce
|
|
108
|
-
- Text sits over a clearer pane than under glass. Over busy content, check that your text still reads
|
|
122
|
+
- Under `prefers-reduced-transparency: reduce`, and in forced colors, the blur and the lens go and the surface is opaque, as under glass.
|
|
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%.
|
|
124
|
+
|
|
125
|
+
#### Liquid buttons
|
|
126
|
+
|
|
127
|
+
A button is a liquid pane when it carries `.glass-liquid` itself, inside a glass region. The modifier on the region alone leaves every button solid: a blur and a lens under each control of a dense toolbar cost two composited layers apiece and read as noise, so the one button that is meant to be glass says so.
|
|
128
|
+
|
|
129
|
+
```html
|
|
130
|
+
<section class="glass glass-liquid">
|
|
131
|
+
<button class="btn primary">Solid, like every control</button>
|
|
132
|
+
<button class="btn ghost glass-liquid">A clear pane</button>
|
|
133
|
+
<button class="btn soft success glass-liquid">A tinted pane</button>
|
|
134
|
+
</section>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- The fill is still [presentation's](./composing.md#presentation). `.ghost` is the clear pane and `.soft` the tinted one; a `.solid` button keeps its opaque plate and takes the rim and sheen over it. Intent, size, `.pill`, `.icon`, hover, and press all work as on any button.
|
|
138
|
+
- It works in a plain `.glass` region too, and on a button inside a liquid card.
|
|
139
|
+
- It spends the button's `::before` and `::after`, so a liquid button cannot carry one of your own.
|
|
140
|
+
- A disabled liquid button keeps the ground and the rim and loses the blur and the lens: it is dimmed with `opacity`, which cuts its layers off from the page behind it.
|
|
141
|
+
- The label sits over whatever is behind the button. Over busy content, check that it still reads, or use `.solid`.
|
|
142
|
+
- It reaches `.btn`, and a bare `<button>` only through its `.btn` class: the layers are a list of classes, which `/native`'s element mapping does not join.
|
|
109
143
|
|
|
110
144
|
## Pixel
|
|
111
145
|
|
|
@@ -119,15 +153,20 @@ The lens is drawn by an SVG filter in `backdrop-filter`, which Chromium renders
|
|
|
119
153
|
|
|
120
154
|
**Exceptions:**
|
|
121
155
|
|
|
122
|
-
- Corners are one unit or nothing.
|
|
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.
|
|
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.
|
|
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.
|
|
123
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.
|
|
124
|
-
- The
|
|
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.
|
|
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.
|
|
163
|
+
- `.radio` and `.tooltip-icon` are never stepped. The circle is what tells a radio from a checkbox at a glance, so a radio stays round. A stepped 20px tooltip trigger would look bitten, so it is a plain square.
|
|
125
164
|
- `--font-pixel` is yours to supply. The package ships no font binary, so the aesthetic has no network side effect and falls back to the monospace stack.
|
|
126
|
-
-
|
|
165
|
+
- Whether digits are easy to read depends on the font you pick. At small sizes, such as page numbers, counters, and badges, some pixel fonts draw `2` and `8`, or `3` and `9`, so alike that they are misread. The CSS cannot fix that. Where small numbers matter, choose a font with clearly different digits.
|
|
127
166
|
|
|
128
167
|
## Chunky tile
|
|
129
168
|
|
|
130
|
-
Rounded
|
|
169
|
+
Rounded tiles seated on a darker bar of their own color, pressed flat on click. The look mobile learning and game apps use; it is named for what it is made of rather than for any one product.
|
|
131
170
|
|
|
132
171
|
`--tile-lift` is how deep the bar is and how far a press travels, `4px` by default. One number drives both, so the top edge drops by exactly the bar's depth and the bottom edge does not move. `--tile-radius` is the corner, `0.75rem`, and it is one value rather than a control/surface pair on purpose — the shared corner is what makes a button and the card it sits in read as the same object.
|
|
133
172
|
|
|
@@ -137,11 +176,11 @@ Rounded slabs sitting on a darker shade of themselves, pressed flat on click. Th
|
|
|
137
176
|
</section>
|
|
138
177
|
```
|
|
139
178
|
|
|
140
|
-
The bar is a darker shade of the element's own
|
|
179
|
+
The bar is a darker shade of the element's own color rather than a shadow under it, so it follows every intent: a success button sits on a dark green edge, a destructive one on a dark red edge, and a plain card on a gray edge that matches the line it already draws. Measured on the light palette, `.btn.success` is `#007a55` on `#004c34`. On a dark page a near-black surface cannot go darker, so its bar lands lighter and reads as a rim.
|
|
141
180
|
|
|
142
181
|
**Exceptions:**
|
|
143
182
|
|
|
144
|
-
- 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:
|
|
145
184
|
|
|
146
185
|
```html
|
|
147
186
|
<div class="card soft edgeless info flat">Promo panel, deliberately flat</div>
|
|
@@ -150,21 +189,22 @@ The bar is a darker shade of the element's own colour rather than a shadow under
|
|
|
150
189
|
- `--font-rounded` is yours to supply. A heavy rounded grotesque is most of this look and the package ships no font binary, so with nothing supplied the page's own stack is used.
|
|
151
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.
|
|
152
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.
|
|
153
|
-
- The shipped `.primary` is
|
|
154
|
-
-
|
|
155
|
-
-
|
|
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.
|
|
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.
|
|
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.
|
|
156
196
|
|
|
157
197
|
## Cyber
|
|
158
198
|
|
|
159
|
-
|
|
199
|
+
Beveled corners, a 1px edge, and a glow in the component's own color: a success button glows green, a destructive card red. The bevel is drawn with `corner-shape`, so the border, the glow, and the focus outline all follow the cut rather than being clipped by it.
|
|
160
200
|
|
|
161
|
-
Controls and surfaces take the cut on opposite diagonals, so a button and the card around it read as the same material and as different things: buttons and fields cut top-left and bottom-right, cards, panels, and alerts top-right and bottom-left. Anything fully round cuts to points
|
|
201
|
+
Controls and surfaces take the cut on opposite diagonals, so a button and the card around it read as the same material and as different things: buttons and fields cut top-left and bottom-right, cards, panels, and alerts top-right and bottom-left. Anything fully round cuts to points: a radio, a switch knob, and the tooltip icon become diamonds, and a pill button or a badge becomes a pointed hexagon. Chips go square, so the checkbox reads as a plain square beside the diamond radio.
|
|
162
202
|
|
|
163
|
-
`--cyber-cut` is the cut at the default size, `0.625rem`. Controls scale it with their size step, so a `.p-xs` icon button takes half of it and keeps its corners, and it stays a true 45 degrees. `--cyber-shape` and `--cyber-shape-surface` replace the default placement with any `border-radius` value, used as written. `--cyber-glow` is the glow's blur, `8px`. `--cyber-ink` is the neutral line and glow
|
|
203
|
+
`--cyber-cut` is the cut at the default size, `0.625rem`. Controls scale it with their size step, so a `.p-xs` icon button takes half of it and keeps its corners, and it stays a true 45 degrees. `--cyber-shape` and `--cyber-shape-surface` replace the default placement with any `border-radius` value, used as written. `--cyber-glow` is the glow's blur, `8px`. `--cyber-ink` is the neutral line and glow color. By default it follows the theme, near-black on a light page and near-white on a dark one; set it for a neon neutral.
|
|
164
204
|
|
|
165
205
|
```html
|
|
166
206
|
<section class="cyber" style="--cyber-ink: rgb(0 229 255); --cyber-cut: 0.75rem">
|
|
167
|
-
<button class="btn">
|
|
207
|
+
<button class="btn">Open terminal</button>
|
|
168
208
|
</section>
|
|
169
209
|
```
|
|
170
210
|
|
|
@@ -189,7 +229,7 @@ A parallelogram slants its sides into the content, so give a surface that takes
|
|
|
189
229
|
**Exceptions:**
|
|
190
230
|
|
|
191
231
|
- The bevel is Chromium-only today. Firefox and Safari do not draw `corner-shape` yet, and there the aesthetic squares its cut corners instead of rounding them, and what is fully round stays round, so the look degrades to square, lined, and glowing. It picks up the bevel with no change once an engine ships it.
|
|
192
|
-
- The glow is a halo, not depth, so every component takes it
|
|
232
|
+
- The glow is a halo, not depth, so every component takes it, fields, badges, and alerts included, and `.flat` does not take it off. `.raised` and `.floating` add no shadow under this aesthetic.
|
|
193
233
|
- In the light theme the glow of a neutral or `.primary` component is its near-black ink, which reads as a soft shadow rather than as light. The look is at its strongest on a dark page, or with a hued `--cyber-ink`.
|
|
194
234
|
- A plain `.btn.icon` is a square control, so it takes the control diagonal; a `.btn.icon.pill` is round, so it becomes a diamond.
|
|
195
235
|
- `--font-cyber` is yours to supply. The package ships no font binary, so the aesthetic falls back to the monospace stack.
|
|
@@ -224,42 +264,18 @@ The dashed button is `ghost edged` because a filled button has no separate line
|
|
|
224
264
|
|
|
225
265
|
**Exceptions:**
|
|
226
266
|
|
|
227
|
-
- 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.
|
|
228
|
-
- Chips
|
|
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.
|
|
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.
|
|
229
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.
|
|
230
270
|
- `--font-sketch` is yours to supply. The package ships no handwriting font and falls back to the platform `cursive` family.
|
|
231
|
-
- Buttons press with the plain `scale(0.97)`; reduced motion
|
|
232
|
-
-
|
|
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`.
|
|
273
|
+
- Forced-colors mode draws the dashed accents solid, like every other material line.
|
|
233
274
|
|
|
234
275
|
## Solo classes
|
|
235
276
|
|
|
236
|
-
An aesthetic class
|
|
237
|
-
|
|
238
|
-
| Aesthetic | Solo class | Paints |
|
|
239
|
-
| --------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
240
|
-
| `.glass` | `.glass-solo` | Translucent ground, hairline edge, blur, the tucked two-layer shadow, the surface corner. With `.glass-liquid`, the liquid pane on its `::before` and `::after`. |
|
|
241
|
-
| `.neobrutalism` | `.neobrutalism-solo` | 2px ink edge, square corners, the hard offset slab. |
|
|
242
|
-
| `.pixel` | `.pixel-solo` | The stepped silhouette, the inset ring in place of a border, the pixel font. |
|
|
243
|
-
| `.chunky-tile` | `.chunky-tile-solo` | The tile corner, 2px edge, the bar under it, the rounded font. |
|
|
244
|
-
| `.cyber` | `.cyber-solo` | The surface diagonal (the control one on an action), 1px edge, the glow, the font. |
|
|
245
|
-
| `.sketch` | `.sketch-solo` | Uneven elliptical corners (sharp surface outline on a pane, expressive control outline on an action; subtle rounded contours when paired with `.sketch-rounded`), a 1px solid ink line, the 2px offset shadow, the handwriting face. |
|
|
246
|
-
|
|
247
|
-
```css
|
|
248
|
-
/* The only stylesheet from this package on the page. */
|
|
249
|
-
@import "@codenhub/styles/aesthetics/glass";
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
```html
|
|
253
|
-
<div class="toast glass-solo">A pane over whatever is behind it.</div>
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
A solo class ships in the same entrypoint as its aesthetic, and needs nothing else loaded:
|
|
277
|
+
An aesthetic class changes nothing on an element this package does not style, such as a toast from another library. Each aesthetic also ships a solo class, such as `.glass-solo`, that paints its look onto that one element. [Solo classes](./solo.md) covers them.
|
|
257
278
|
|
|
258
|
-
|
|
259
|
-
- **It reads its aesthetic's knobs and the theme's tokens, with the shipped values as fallbacks.** `--glass-radius-surface`, `--neo-offset`, `--pixel-unit`, `--tile-lift`, `--tile-radius`, `--cyber-cut`, `--cyber-shape`, `--cyber-shape-surface`, `--cyber-glow`, `--cyber-ink`, `--sketch-radius`, `--sketch-radius-surface`, `--sketch-radius-rounded`, `--sketch-radius-surface-rounded`, and the font knobs (including `--font-sketch`) all work, set on the element or any ancestor. With the theme loaded it follows it; without it, it renders the shipped look.
|
|
260
|
-
- **It ignores the aesthetic around it.** It reads no `--ui-*` or `--elevation-color`, so a `.glass-solo` inside a `.pixel` region keeps its corners. The elevation modifiers do not reach it either.
|
|
261
|
-
- **It beats a foreign component's own styles.** The rules are unlayered and one class deep, so they win over a component's zero-specificity or layered rules wherever the two load. The flip side is that a Tailwind utility on the same element (`rounded-none`) loses to it: tune a solo class through its knobs.
|
|
262
|
-
- **It presses only an action.** Neobrutalism, chunky tile, and cyber press a `button`, `a[href]`, `[role="button"]`, `summary`, or button-type `input` that is not disabled; a container stays put, so a toast does not sink when clicked. Reduced motion drops the movement. Chunky tile's heavier label follows the same rule.
|
|
263
|
-
- **Dark values need a colour scheme.** Light and dark pairs are `light-dark()`, which follows the element's `color-scheme`. The package theme sets it; a page without it gets the light values.
|
|
279
|
+
## Next
|
|
264
280
|
|
|
265
|
-
|
|
281
|
+
[Solo classes](./solo.md) applies these looks to components from other libraries. If every component on your screen comes from this package, the guides end here: [Composing](./composing.md), [Customizing](./customizing.md), and [Content and layout](./content-and-layout.md) are the lookup pages, and [Accessibility](../accessibility.md) lists what your application still owes its users.
|
package/docs/usage/buttons.md
CHANGED
|
@@ -1,62 +1,134 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Buttons
|
|
3
|
-
description:
|
|
4
|
-
order:
|
|
3
|
+
description: Destructive and secondary actions, icon buttons, sizes, link buttons, disabled states, and loading icons.
|
|
4
|
+
order: 1
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Buttons
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The settings screen has a Save button and a Cancel button. This page adds the rest of its actions: a danger zone with a destructive Delete button, an icon button that copies an API key, and smaller buttons for a dense row. It also covers disabled buttons and how to load the icons used here and on later pages.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Add a danger zone
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
A destructive action goes in its own card at the bottom of the screen. The card is soft and destructive so it reads as a warning; the button is solid so it is the loudest thing in that card:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
```html
|
|
16
|
+
<section class="card soft destructive stack" aria-labelledby="danger-title">
|
|
17
|
+
<h2 class="text-title-sm" id="danger-title">Delete account</h2>
|
|
18
|
+
<p>This removes your projects, invoices, and API keys. It cannot be undone.</p>
|
|
19
|
+
<div class="cluster">
|
|
20
|
+
<button class="btn destructive" type="button">Delete account</button>
|
|
21
|
+
<button class="btn ghost" type="button">Export data first</button>
|
|
22
|
+
</div>
|
|
23
|
+
</section>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The card and the button each name `.destructive`, because [intent does not pass down](../concepts.md#intent-which-color). The Export button keeps its neutral color inside the red card for the same reason.
|
|
27
|
+
|
|
28
|
+
## Choose a fill and an edge
|
|
29
|
+
|
|
30
|
+
A button accepts one intent, one fill class, and one edge class, all optional. With none, a button is solid and edgeless:
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<button class="btn primary">Save changes</button>
|
|
34
|
+
<button class="btn secondary">Preview</button>
|
|
35
|
+
<button class="btn primary soft">Invite member</button>
|
|
36
|
+
<button class="btn destructive ghost edged">Revoke key</button>
|
|
37
|
+
<button class="btn ghost">Cancel</button>
|
|
38
|
+
```
|
|
39
|
+
|
|
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
|
+
|
|
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.
|
|
24
43
|
|
|
25
|
-
|
|
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.
|
|
26
45
|
|
|
27
|
-
|
|
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`.
|
|
28
47
|
|
|
29
|
-
|
|
48
|
+
## Sizes and shapes
|
|
30
49
|
|
|
31
|
-
| Class
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
| `.sm`
|
|
35
|
-
| `.lg`
|
|
36
|
-
| `.
|
|
37
|
-
| `.
|
|
38
|
-
| `.p-
|
|
39
|
-
| `.
|
|
40
|
-
| `.
|
|
50
|
+
| Class | Effect |
|
|
51
|
+
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| _none_ | At least 2.5rem tall (`--control-height`), 1rem side padding, and the same font size as the surrounding text. |
|
|
53
|
+
| `.sm` | At least 2rem tall, 0.75rem side padding, 0.875rem text. |
|
|
54
|
+
| `.lg` | At least 3rem tall, 1.25rem side padding, 1.0625rem text. |
|
|
55
|
+
| `.pill` | Fully rounded ends. |
|
|
56
|
+
| `.icon` | Square. At least 2.5rem on each side, 2rem with `.sm`, 3rem with `.lg`. |
|
|
57
|
+
| `.p-xs` | Tightest padding (0.5rem sides, 0.125rem top and bottom). Also removes the minimum height, so a button fits a table cell or a dense toolbar. |
|
|
58
|
+
| `.p-sm` | Compact padding (0.625rem sides, 0.25rem top and bottom). |
|
|
59
|
+
| `.p-lg` | Roomy padding (1.5rem sides, 0.75rem top and bottom). |
|
|
41
60
|
|
|
42
|
-
|
|
61
|
+
A size class and a padding class can be combined. Only `.p-xs` removes the minimum height of a labeled button; every other combination keeps it. On an icon button, any `.p-*` class removes the minimum size and the button sizes to its icon plus that padding: 0.125rem, 0.25rem, or 0.75rem around it.
|
|
62
|
+
|
|
63
|
+
The base look draws corners that scale with the size: a `.sm` or `.p-sm` button has slightly smaller corners than a default one, and `.lg` slightly larger.
|
|
64
|
+
|
|
65
|
+
## Icon buttons
|
|
66
|
+
|
|
67
|
+
The API keys list on the settings screen has a copy button beside each key. An icon-only button needs an accessible name, because the icon has no text a screen reader can read:
|
|
43
68
|
|
|
44
69
|
```html
|
|
45
|
-
<button class="btn
|
|
46
|
-
<
|
|
47
|
-
<button class="btn warning soft">Warning soft</button>
|
|
48
|
-
<button class="btn destructive ghost edgeless">Danger ghost</button>
|
|
49
|
-
<button class="btn icon primary" aria-label="Create">+</button>
|
|
50
|
-
<button class="btn primary" disabled>
|
|
51
|
-
<span class="loader sm" aria-hidden="true"></span>
|
|
52
|
-
Saving
|
|
70
|
+
<button class="btn icon ghost" type="button" aria-label="Copy API key">
|
|
71
|
+
<i class="ic-lucide-copy" aria-hidden="true"></i>
|
|
53
72
|
</button>
|
|
73
|
+
<button class="btn ghost sm" type="button">
|
|
74
|
+
<i class="ic-lucide-download" aria-hidden="true"></i>
|
|
75
|
+
Download invoice
|
|
76
|
+
</button>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A button is a flex row with a 0.5rem gap, so an icon next to a label is spaced without extra markup.
|
|
80
|
+
|
|
81
|
+
### Loading icons
|
|
82
|
+
|
|
83
|
+
This package ships no icons. The examples in these docs use `@codenhub/icons` with the Lucide family, and its class names are always written with the family, as `ic-lucide-copy`. Install it and load it once, next to this package:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
pnpm add @codenhub/icons
|
|
54
87
|
```
|
|
55
88
|
|
|
56
|
-
|
|
89
|
+
With compiled CSS, import the base rules and each family you use:
|
|
90
|
+
|
|
91
|
+
```css
|
|
92
|
+
@import "@codenhub/styles";
|
|
93
|
+
@import "@codenhub/icons";
|
|
94
|
+
@import "@codenhub/icons/lucide";
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
With Tailwind source, import its Tailwind entrypoint after this package's:
|
|
98
|
+
|
|
99
|
+
```css
|
|
100
|
+
@import "@codenhub/styles/tw";
|
|
101
|
+
@import "@codenhub/icons/tw";
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Any other icon works too: an inline `<svg>` with `stroke="currentColor"` or `fill="currentColor"` takes the button's text color the same way.
|
|
105
|
+
|
|
106
|
+
## Links that look like buttons
|
|
107
|
+
|
|
108
|
+
A link that navigates, such as "View billing history", is still a link. Write it as `<a href>` with `.btn`, and it gets the same look:
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<a class="btn secondary ghost edged" href="/settings/billing">View billing history</a>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Do not use `<div class="btn">` or `<span class="btn">`: they look like buttons but cannot be focused or activated from a keyboard. Use `<button>` for actions and `<a href>` for navigation.
|
|
115
|
+
|
|
116
|
+
## Disabled buttons
|
|
117
|
+
|
|
118
|
+
Any of these makes a button look disabled: the `disabled` attribute, `.disabled`, `aria-disabled="true"`, or `data-disabled`. A disabled button is drawn at 58% opacity with a not-allowed cursor, and does not change on hover or press.
|
|
119
|
+
|
|
120
|
+
```html
|
|
121
|
+
<button class="btn primary" type="submit" disabled>Save changes</button>
|
|
122
|
+
|
|
123
|
+
<a class="btn ghost" aria-disabled="true">Download invoice</a>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Only the `disabled` attribute stops the button from working. `.disabled`, `aria-disabled`, and `data-disabled` change the look only: a click or the Enter key still activates it, so your code has to ignore it. `aria-disabled` is the right choice when the button should stay focusable, for example so a tooltip can explain why it is disabled. A link has no `disabled` attribute, so remove its `href` as well when it should not navigate.
|
|
127
|
+
|
|
128
|
+
## Loading state
|
|
57
129
|
|
|
58
|
-
|
|
130
|
+
There is no loading class. A loading button is a disabled button with a spinner icon you bring, which [Feedback](./feedback.md#show-that-the-form-is-saving) builds for the Save button. Disable it so a second click cannot submit twice.
|
|
59
131
|
|
|
60
|
-
|
|
132
|
+
## Next
|
|
61
133
|
|
|
62
|
-
|
|
134
|
+
The buttons are ready; the card they save has nothing to save yet. [Forms](./forms.md) adds the profile fields.
|