@echodial/deck 0.1.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/LICENSE +21 -0
- package/README.md +1327 -0
- package/bin/deck.mjs +219 -0
- package/build.mjs +338 -0
- package/dist/api-buckets.json +10486 -0
- package/dist/api.json +57896 -0
- package/dist/brand/deck-apple-touch-icon.png +0 -0
- package/dist/brand/deck-logo-dark.svg +1 -0
- package/dist/brand/deck-logo-light.svg +1 -0
- package/dist/brand/deck-logo-stacked.svg +1 -0
- package/dist/brand/deck-logo.svg +1 -0
- package/dist/brand/deck-mark.svg +7 -0
- package/dist/brand/deck-og.png +0 -0
- package/dist/collisions.json +223 -0
- package/dist/deck-adapters.js +482 -0
- package/dist/deck-adapters.min.js +23 -0
- package/dist/deck-extras.js +823 -0
- package/dist/deck-extras.min.js +5 -0
- package/dist/deck-icons.svg +184 -0
- package/dist/deck.bundle.js +2638 -0
- package/dist/deck.bundle.min.js +86 -0
- package/dist/deck.css +7904 -0
- package/dist/deck.esm.js +2644 -0
- package/dist/deck.js +1331 -0
- package/dist/deck.min.css +32 -0
- package/dist/deck.min.js +60 -0
- package/dist/layers/anchor.css +192 -0
- package/dist/layers/buttons.css +210 -0
- package/dist/layers/charts.css +337 -0
- package/dist/layers/combobox.css +248 -0
- package/dist/layers/components.css +868 -0
- package/dist/layers/container.css +199 -0
- package/dist/layers/datagrid.css +335 -0
- package/dist/layers/datepicker.css +305 -0
- package/dist/layers/forms.css +401 -0
- package/dist/layers/gradients.css +302 -0
- package/dist/layers/inputs.css +522 -0
- package/dist/layers/layers.css +87 -0
- package/dist/layers/layout.css +235 -0
- package/dist/layers/logical.css +226 -0
- package/dist/layers/media.css +400 -0
- package/dist/layers/mobile.css +245 -0
- package/dist/layers/motion.css +379 -0
- package/dist/layers/nav.css +771 -0
- package/dist/layers/perf.css +234 -0
- package/dist/layers/print.css +198 -0
- package/dist/layers/reset.css +103 -0
- package/dist/layers/space3d.css +280 -0
- package/dist/layers/toasts.css +201 -0
- package/dist/layers/tokens.css +229 -0
- package/dist/layers/type.css +114 -0
- package/dist/layers/utilities.css +241 -0
- package/dist/sizes.json +52 -0
- package/dist/usage.json +6340 -0
- package/package.json +94 -0
- package/src/00-layers.css +87 -0
- package/src/01-tokens.css +229 -0
- package/src/02-reset.css +103 -0
- package/src/03-type.css +114 -0
- package/src/04-layout.css +235 -0
- package/src/05-buttons.css +210 -0
- package/src/06-forms.css +401 -0
- package/src/07-components.css +868 -0
- package/src/08-mobile.css +245 -0
- package/src/09-utilities.css +241 -0
- package/src/10-datepicker.css +305 -0
- package/src/11-combobox.css +248 -0
- package/src/12-datagrid.css +335 -0
- package/src/13-toasts.css +201 -0
- package/src/14-charts.css +337 -0
- package/src/16-motion.css +379 -0
- package/src/18-container.css +199 -0
- package/src/19-logical.css +226 -0
- package/src/20-gradients.css +302 -0
- package/src/21-space3d.css +280 -0
- package/src/22-nav.css +771 -0
- package/src/23-inputs.css +522 -0
- package/src/24-media.css +400 -0
- package/src/25-anchor.css +192 -0
- package/src/26-perf.css +234 -0
- package/src/99-print.css +198 -0
- package/src/brand/deck-apple-touch-icon.png +0 -0
- package/src/brand/deck-logo-dark.svg +1 -0
- package/src/brand/deck-logo-light.svg +1 -0
- package/src/brand/deck-logo-stacked.svg +1 -0
- package/src/brand/deck-logo.svg +1 -0
- package/src/brand/deck-mark.svg +7 -0
- package/src/brand/deck-og.png +0 -0
- package/src/brand/sources.json +7 -0
- package/src/deck-icons.svg +184 -0
- package/src/js/deck-adapters.js +482 -0
- package/src/js/deck-extras.js +823 -0
- package/src/js/deck.js +1331 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Deck — 25 Anchor positioning
|
|
3
|
+
Every floating thing in Deck was positioned by hand with
|
|
4
|
+
getBoundingClientRect, which does not flip when it hits the bottom of the
|
|
5
|
+
window, does not shift back inside when it hits an edge, and does not
|
|
6
|
+
follow its anchor inside a scrolling container. Those are exactly the
|
|
7
|
+
problems Floating UI solves, and they are also exactly what CSS anchor
|
|
8
|
+
positioning solves natively — with no JavaScript, on the compositor, and
|
|
9
|
+
without a resize or scroll listener.
|
|
10
|
+
|
|
11
|
+
So Deck uses anchor positioning where the browser has it, and hands off to
|
|
12
|
+
Floating UI where it does not, if you have loaded it. Neither is required.
|
|
13
|
+
========================================================================== */
|
|
14
|
+
|
|
15
|
+
@layer deck.components {
|
|
16
|
+
|
|
17
|
+
@supports (anchor-name: --a) {
|
|
18
|
+
|
|
19
|
+
/* --- Naming an anchor --------------------------------------------------
|
|
20
|
+
Put .anchor on the trigger and --anchor on both trigger and panel.
|
|
21
|
+
Each pair needs a unique name, so generate it server-side in Keel:
|
|
22
|
+
<button class="anchor" style="--anchor:--menu-<?= $id ?>">
|
|
23
|
+
---------------------------------------------------------------------- */
|
|
24
|
+
.anchor { anchor-name: var(--anchor); }
|
|
25
|
+
|
|
26
|
+
/* --- Menus and mega menus ---------------------------------------------- */
|
|
27
|
+
.menu, .mega, .datepicker {
|
|
28
|
+
position-anchor: var(--anchor);
|
|
29
|
+
position-area: block-end span-inline-end;
|
|
30
|
+
position-try-fallbacks:
|
|
31
|
+
block-end span-inline-start,
|
|
32
|
+
block-start span-inline-end,
|
|
33
|
+
block-start span-inline-start,
|
|
34
|
+
flip-block, flip-inline;
|
|
35
|
+
position-try-order: most-block-size;
|
|
36
|
+
margin-block: var(--space-2);
|
|
37
|
+
/* stay inside the viewport no matter which fallback wins */
|
|
38
|
+
max-block-size: calc(100dvh - var(--space-8));
|
|
39
|
+
max-inline-size: calc(100vw - var(--space-6));
|
|
40
|
+
inset: auto;
|
|
41
|
+
}
|
|
42
|
+
.menu { min-inline-size: max(12rem, anchor-size(inline)); }
|
|
43
|
+
|
|
44
|
+
.mega {
|
|
45
|
+
position-area: block-end;
|
|
46
|
+
justify-self: anchor-center;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* A dropdown that should match the width of the field it belongs to */
|
|
50
|
+
.menu-match { inline-size: anchor-size(inline); min-inline-size: 0; }
|
|
51
|
+
|
|
52
|
+
/* --- Combobox list ------------------------------------------------------
|
|
53
|
+
The listbox is a sibling today, positioned with absolute. With anchor
|
|
54
|
+
positioning it can go in the top layer and escape overflow:hidden
|
|
55
|
+
ancestors, which is the bug that bites every combobox inside a modal.
|
|
56
|
+
---------------------------------------------------------------------- */
|
|
57
|
+
.combo-control { anchor-name: var(--anchor, --combo); }
|
|
58
|
+
.combo-list {
|
|
59
|
+
position-anchor: var(--anchor, --combo);
|
|
60
|
+
position-area: block-end;
|
|
61
|
+
position-try-fallbacks: flip-block;
|
|
62
|
+
inline-size: anchor-size(inline);
|
|
63
|
+
inset: auto;
|
|
64
|
+
margin-block: 4px;
|
|
65
|
+
}
|
|
66
|
+
.combo-list.is-above { inset: auto; }
|
|
67
|
+
|
|
68
|
+
/* --- Tooltips -----------------------------------------------------------
|
|
69
|
+
The ::after tooltip could never flip. This one can, and it can carry a
|
|
70
|
+
real arrow that stays pointed at the anchor.
|
|
71
|
+
---------------------------------------------------------------------- */
|
|
72
|
+
.tip {
|
|
73
|
+
position: fixed;
|
|
74
|
+
position-anchor: var(--anchor);
|
|
75
|
+
position-area: block-start;
|
|
76
|
+
position-try-fallbacks: flip-block, flip-inline;
|
|
77
|
+
justify-self: anchor-center;
|
|
78
|
+
margin: var(--space-2);
|
|
79
|
+
inline-size: max-content;
|
|
80
|
+
max-inline-size: 16rem;
|
|
81
|
+
padding: var(--space-2) var(--space-3);
|
|
82
|
+
border: 0;
|
|
83
|
+
border-radius: var(--r-xs);
|
|
84
|
+
background: var(--ink-900);
|
|
85
|
+
color: var(--ink-50);
|
|
86
|
+
font-size: var(--text-xs);
|
|
87
|
+
line-height: 1.4;
|
|
88
|
+
box-shadow: var(--shadow-3);
|
|
89
|
+
opacity: 0;
|
|
90
|
+
transition: opacity var(--dur-2) var(--ease-out),
|
|
91
|
+
overlay var(--dur-2) allow-discrete,
|
|
92
|
+
display var(--dur-2) allow-discrete;
|
|
93
|
+
}
|
|
94
|
+
.tip:popover-open { opacity: 1; }
|
|
95
|
+
@starting-style { .tip:popover-open { opacity: 0; } }
|
|
96
|
+
.tip::backdrop { background: transparent; }
|
|
97
|
+
|
|
98
|
+
/* The arrow on a .tip. background: inherit takes the tip's colour, and
|
|
99
|
+
justify-self: anchor-center keeps it pointed at the trigger whichever way
|
|
100
|
+
the tip has flipped. */
|
|
101
|
+
.tip-arrow {
|
|
102
|
+
position: absolute;
|
|
103
|
+
inline-size: 8px;
|
|
104
|
+
block-size: 8px;
|
|
105
|
+
rotate: 45deg;
|
|
106
|
+
background: inherit;
|
|
107
|
+
position-anchor: var(--anchor);
|
|
108
|
+
justify-self: anchor-center;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* --- Popover card -------------------------------------------------------
|
|
112
|
+
A richer tooltip: a heading, body copy, and actions. Used for the
|
|
113
|
+
"what does this labor code mean" pattern.
|
|
114
|
+
---------------------------------------------------------------------- */
|
|
115
|
+
.pop {
|
|
116
|
+
position: fixed;
|
|
117
|
+
position-anchor: var(--anchor);
|
|
118
|
+
position-area: block-end;
|
|
119
|
+
position-try-fallbacks: flip-block, flip-inline, block-start span-inline-start;
|
|
120
|
+
justify-self: anchor-center;
|
|
121
|
+
margin: var(--space-2);
|
|
122
|
+
inline-size: min(20rem, calc(100vw - var(--space-6)));
|
|
123
|
+
padding: var(--space-4);
|
|
124
|
+
background: var(--surface);
|
|
125
|
+
color: var(--text);
|
|
126
|
+
border: 1px solid var(--line);
|
|
127
|
+
border-radius: var(--r-md);
|
|
128
|
+
box-shadow: var(--shadow-4);
|
|
129
|
+
opacity: 0;
|
|
130
|
+
translate: 0 -4px;
|
|
131
|
+
transition: opacity var(--dur-2) var(--ease-out),
|
|
132
|
+
translate var(--dur-2) var(--ease-out),
|
|
133
|
+
overlay var(--dur-2) allow-discrete,
|
|
134
|
+
display var(--dur-2) allow-discrete;
|
|
135
|
+
}
|
|
136
|
+
.pop:popover-open { opacity: 1; translate: 0; }
|
|
137
|
+
@starting-style { .pop:popover-open { opacity: 0; translate: 0 -4px; } }
|
|
138
|
+
.pop::backdrop { background: transparent; }
|
|
139
|
+
/* The heading inside a popover card. */
|
|
140
|
+
.pop-title { font-weight: 620; margin-block-end: var(--space-1); }
|
|
141
|
+
/* The explanatory text inside a popover card. */
|
|
142
|
+
.pop-body { font-size: var(--text-sm); color: var(--text-muted); }
|
|
143
|
+
/* The action row at the bottom of a popover card. A button here can close
|
|
144
|
+
the panel with popovertargetaction="hide" and no script. */
|
|
145
|
+
.pop-actions { display: flex; gap: var(--space-2); margin-block-start: var(--space-3); }
|
|
146
|
+
|
|
147
|
+
/* --- Hide the anchored element when its anchor scrolls out of view ------ */
|
|
148
|
+
.menu, .mega, .tip, .pop, .datepicker, .combo-list {
|
|
149
|
+
position-visibility: anchors-visible;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* --- Floating UI handoff --------------------------------------------------
|
|
154
|
+
Where anchor positioning is missing, deck-adapters.js uses Floating UI if
|
|
155
|
+
it is on the page and falls back to Deck's own placement if it is not.
|
|
156
|
+
These rules just get out of the way so the adapter can set inset.
|
|
157
|
+
---------------------------------------------------------------------- */
|
|
158
|
+
@supports not (anchor-name: --a) {
|
|
159
|
+
.tip, .pop {
|
|
160
|
+
position: fixed;
|
|
161
|
+
inset: auto;
|
|
162
|
+
margin: 0;
|
|
163
|
+
inline-size: max-content;
|
|
164
|
+
max-inline-size: 16rem;
|
|
165
|
+
border: 0;
|
|
166
|
+
border-radius: var(--r-xs);
|
|
167
|
+
background: var(--ink-900);
|
|
168
|
+
color: var(--ink-50);
|
|
169
|
+
font-size: var(--text-xs);
|
|
170
|
+
padding: var(--space-2) var(--space-3);
|
|
171
|
+
box-shadow: var(--shadow-3);
|
|
172
|
+
opacity: 0;
|
|
173
|
+
transition: opacity var(--dur-2) var(--ease-out),
|
|
174
|
+
overlay var(--dur-2) allow-discrete,
|
|
175
|
+
display var(--dur-2) allow-discrete;
|
|
176
|
+
}
|
|
177
|
+
.pop {
|
|
178
|
+
inline-size: min(20rem, calc(100vw - var(--space-6)));
|
|
179
|
+
max-inline-size: none;
|
|
180
|
+
background: var(--surface);
|
|
181
|
+
color: var(--text);
|
|
182
|
+
border: 1px solid var(--line);
|
|
183
|
+
border-radius: var(--r-md);
|
|
184
|
+
box-shadow: var(--shadow-4);
|
|
185
|
+
padding: var(--space-4);
|
|
186
|
+
}
|
|
187
|
+
.tip:popover-open, .pop:popover-open { opacity: 1; }
|
|
188
|
+
@starting-style { .tip:popover-open, .pop:popover-open { opacity: 0; } }
|
|
189
|
+
.tip::backdrop, .pop::backdrop { background: transparent; }
|
|
190
|
+
.tip-arrow { position: absolute; inline-size: 8px; block-size: 8px; rotate: 45deg; background: inherit; }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Deck — 05 Buttons
|
|
3
|
+
========================================================================== */
|
|
4
|
+
|
|
5
|
+
@layer deck.components {
|
|
6
|
+
|
|
7
|
+
.btn {
|
|
8
|
+
--btn-bg: var(--surface);
|
|
9
|
+
--btn-text: var(--text);
|
|
10
|
+
--btn-border: var(--line-strong);
|
|
11
|
+
--btn-shadow: var(--shadow-1);
|
|
12
|
+
|
|
13
|
+
display: inline-flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
gap: var(--space-2);
|
|
17
|
+
block-size: var(--control-h);
|
|
18
|
+
min-inline-size: var(--control-h);
|
|
19
|
+
padding-inline: var(--space-4);
|
|
20
|
+
border: 1px solid var(--btn-border);
|
|
21
|
+
border-radius: var(--r-sm);
|
|
22
|
+
background: var(--btn-bg);
|
|
23
|
+
color: var(--btn-text);
|
|
24
|
+
box-shadow: var(--btn-shadow);
|
|
25
|
+
font-size: var(--text-base);
|
|
26
|
+
font-weight: 560;
|
|
27
|
+
line-height: 1;
|
|
28
|
+
letter-spacing: -.008em;
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
user-select: none;
|
|
33
|
+
-webkit-tap-highlight-color: transparent;
|
|
34
|
+
touch-action: manipulation;
|
|
35
|
+
transition: background-color var(--dur-1) var(--ease-out),
|
|
36
|
+
border-color var(--dur-1) var(--ease-out),
|
|
37
|
+
color var(--dur-1) var(--ease-out),
|
|
38
|
+
box-shadow var(--dur-1) var(--ease-out),
|
|
39
|
+
translate var(--dur-1) var(--ease-out);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Every button clears the 44px touch target even when it looks smaller */
|
|
43
|
+
.btn { position: relative; }
|
|
44
|
+
.btn::before {
|
|
45
|
+
content: "";
|
|
46
|
+
position: absolute;
|
|
47
|
+
inset-block-start: 50%;
|
|
48
|
+
inset-inline-start: 50%;
|
|
49
|
+
translate: -50% -50%;
|
|
50
|
+
inline-size: 100%;
|
|
51
|
+
block-size: 100%;
|
|
52
|
+
min-inline-size: var(--tap);
|
|
53
|
+
min-block-size: var(--tap);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.btn:hover { background: var(--surface-hover); border-color: var(--ink-400); }
|
|
57
|
+
.btn:active { translate: 0 1px; box-shadow: none; }
|
|
58
|
+
.btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
|
|
59
|
+
|
|
60
|
+
/* Styles a link or non-form element as disabled. It cannot stop a click
|
|
61
|
+
the way the disabled attribute does, so add aria-disabled="true" and
|
|
62
|
+
remove the href if it must not be followed. */
|
|
63
|
+
.btn[disabled], .btn[aria-disabled="true"], .btn.is-disabled {
|
|
64
|
+
opacity: .5;
|
|
65
|
+
cursor: not-allowed;
|
|
66
|
+
pointer-events: none;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* --- Variants ----------------------------------------------------------- */
|
|
70
|
+
/* The one important action in a view. Solid brand fill; use exactly one
|
|
71
|
+
per screen so it keeps meaning something. */
|
|
72
|
+
.btn-primary {
|
|
73
|
+
--btn-bg: var(--brand-600);
|
|
74
|
+
--btn-text: var(--text-on-brand);
|
|
75
|
+
--btn-border: transparent;
|
|
76
|
+
--btn-shadow: var(--shadow-2), var(--shadow-inset);
|
|
77
|
+
}
|
|
78
|
+
.btn-primary:hover { --btn-bg: var(--brand-700); border-color: transparent; background: var(--brand-700); }
|
|
79
|
+
|
|
80
|
+
/* A secondary emphasis colour, for upsells and highlights. */
|
|
81
|
+
.btn-accent {
|
|
82
|
+
--btn-bg: var(--accent-500);
|
|
83
|
+
--btn-text: var(--text-on-accent);
|
|
84
|
+
--btn-border: transparent;
|
|
85
|
+
--btn-shadow: var(--shadow-2), var(--shadow-inset);
|
|
86
|
+
}
|
|
87
|
+
.btn-accent:hover { background: oklch(from var(--accent-500) calc(l - .05) c h); border-color: transparent; }
|
|
88
|
+
|
|
89
|
+
/* Destructive and irreversible. Pair it with a confirmation. */
|
|
90
|
+
.btn-danger {
|
|
91
|
+
--btn-bg: var(--bad-600);
|
|
92
|
+
--btn-text: var(--text-on-bad);
|
|
93
|
+
--btn-border: transparent;
|
|
94
|
+
--btn-shadow: var(--shadow-2), var(--shadow-inset);
|
|
95
|
+
}
|
|
96
|
+
.btn-danger:hover { background: var(--bad-700); border-color: transparent; }
|
|
97
|
+
|
|
98
|
+
/* Tinted, low weight. Good for an action repeated down a list. */
|
|
99
|
+
.btn-soft {
|
|
100
|
+
--btn-bg: var(--brand-soft);
|
|
101
|
+
--btn-text: var(--brand-soft-text);
|
|
102
|
+
--btn-border: transparent;
|
|
103
|
+
--btn-shadow: none;
|
|
104
|
+
}
|
|
105
|
+
.btn-soft:hover { background: color-mix(in oklab, var(--brand-soft) 80%, var(--brand-300)); border-color: transparent; }
|
|
106
|
+
|
|
107
|
+
/* No border or background until hover. For toolbars and dense rows where
|
|
108
|
+
seven bordered buttons would be noise. */
|
|
109
|
+
.btn-ghost {
|
|
110
|
+
--btn-bg: transparent;
|
|
111
|
+
--btn-border: transparent;
|
|
112
|
+
--btn-shadow: none;
|
|
113
|
+
}
|
|
114
|
+
.btn-ghost:hover { background: var(--surface-hover); border-color: transparent; }
|
|
115
|
+
|
|
116
|
+
/* Brand-coloured border, transparent fill. */
|
|
117
|
+
.btn-outline {
|
|
118
|
+
--btn-bg: transparent;
|
|
119
|
+
--btn-text: var(--brand);
|
|
120
|
+
--btn-border: color-mix(in oklab, var(--brand) 45%, transparent);
|
|
121
|
+
--btn-shadow: none;
|
|
122
|
+
}
|
|
123
|
+
.btn-outline:hover { background: var(--brand-soft); border-color: var(--brand); }
|
|
124
|
+
|
|
125
|
+
/* Looks like a link, behaves like a button. Only for a real <button>
|
|
126
|
+
that has to sit in a row of links — on an anchor it adds a touch
|
|
127
|
+
target, a ring offset and nowrap to something that wanted none of them. */
|
|
128
|
+
.btn-link {
|
|
129
|
+
--btn-bg: transparent;
|
|
130
|
+
--btn-border: transparent;
|
|
131
|
+
--btn-shadow: none;
|
|
132
|
+
--btn-text: var(--brand);
|
|
133
|
+
padding-inline: var(--space-1);
|
|
134
|
+
block-size: auto;
|
|
135
|
+
}
|
|
136
|
+
.btn-link:hover { background: none; border-color: transparent; text-decoration: underline; }
|
|
137
|
+
|
|
138
|
+
/* --- Sizes -------------------------------------------------------------- */
|
|
139
|
+
/* The 34px size. Still clears the 44px touch target, because the target
|
|
140
|
+
is a pseudo-element rather than the box. */
|
|
141
|
+
.btn-sm {
|
|
142
|
+
block-size: var(--control-h-sm);
|
|
143
|
+
padding-inline: var(--space-3);
|
|
144
|
+
font-size: var(--text-sm);
|
|
145
|
+
border-radius: var(--r-xs);
|
|
146
|
+
gap: var(--space-1);
|
|
147
|
+
}
|
|
148
|
+
/* The large size, from --control-h-lg. */
|
|
149
|
+
.btn-lg {
|
|
150
|
+
block-size: var(--control-h-lg);
|
|
151
|
+
padding-inline: var(--space-6);
|
|
152
|
+
font-size: var(--text-md);
|
|
153
|
+
font-weight: 600;
|
|
154
|
+
}
|
|
155
|
+
/* Full width. For a phone, or the primary action at the end of a form. */
|
|
156
|
+
.btn-block { inline-size: 100%; }
|
|
157
|
+
|
|
158
|
+
/* Square, icon-only button. It has no text, so it needs an aria-label. */
|
|
159
|
+
.btn-icon { padding-inline: 0; inline-size: var(--control-h); }
|
|
160
|
+
.btn-icon.btn-sm { inline-size: var(--control-h-sm); }
|
|
161
|
+
.btn-icon.btn-lg { inline-size: var(--control-h-lg); }
|
|
162
|
+
/* A fully rounded button, usually paired with .btn-icon. */
|
|
163
|
+
.btn-round { border-radius: var(--r-full); }
|
|
164
|
+
|
|
165
|
+
/* --- Group -------------------------------------------------------------- */
|
|
166
|
+
/* Buttons joined into one control, with the inner radii and the doubled
|
|
167
|
+
border between them removed. Logical radii, so it mirrors under RTL. */
|
|
168
|
+
.btn-group { display: inline-flex; }
|
|
169
|
+
.btn-group > .btn { border-radius: 0; box-shadow: none; }
|
|
170
|
+
.btn-group > .btn:first-child { border-start-start-radius: var(--r-sm); border-end-start-radius: var(--r-sm); }
|
|
171
|
+
.btn-group > .btn:last-child { border-start-end-radius: var(--r-sm); border-end-end-radius: var(--r-sm); }
|
|
172
|
+
.btn-group > .btn + .btn { margin-inline-start: -1px; }
|
|
173
|
+
.btn-group > .btn:hover, .btn-group > .btn:focus-visible { z-index: 1; }
|
|
174
|
+
|
|
175
|
+
/* --- Loading ------------------------------------------------------------ */
|
|
176
|
+
.btn.is-loading { color: transparent; pointer-events: none; }
|
|
177
|
+
.btn.is-loading::after {
|
|
178
|
+
content: "";
|
|
179
|
+
position: absolute;
|
|
180
|
+
inline-size: 1em;
|
|
181
|
+
block-size: 1em;
|
|
182
|
+
border: 2px solid color-mix(in oklab, var(--btn-text) 30%, transparent);
|
|
183
|
+
border-block-start-color: var(--btn-text);
|
|
184
|
+
border-radius: var(--r-full);
|
|
185
|
+
animation: dk-spin .62s linear infinite;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@keyframes dk-spin { to { rotate: 360deg; } }
|
|
189
|
+
|
|
190
|
+
/* Floating action button, parked above the tab bar and the notch */
|
|
191
|
+
.fab {
|
|
192
|
+
position: fixed;
|
|
193
|
+
inset-block-end: calc(var(--space-5) + env(safe-area-inset-bottom));
|
|
194
|
+
inset-inline-end: var(--space-5);
|
|
195
|
+
z-index: var(--z-nav);
|
|
196
|
+
inline-size: 56px;
|
|
197
|
+
block-size: 56px;
|
|
198
|
+
border-radius: var(--r-full);
|
|
199
|
+
background: var(--brand-600);
|
|
200
|
+
color: var(--text-on-brand);
|
|
201
|
+
box-shadow: var(--shadow-4);
|
|
202
|
+
display: grid;
|
|
203
|
+
place-items: center;
|
|
204
|
+
cursor: pointer;
|
|
205
|
+
transition: scale var(--dur-2) var(--ease-spring), background-color var(--dur-1) var(--ease-out);
|
|
206
|
+
}
|
|
207
|
+
.fab:hover { background: var(--brand-700); scale: 1.04; }
|
|
208
|
+
.fab:active { scale: .96; }
|
|
209
|
+
body:has(.tabbar) .fab { inset-block-end: calc(var(--space-4) + var(--tap) + var(--space-3) + env(safe-area-inset-bottom)); }
|
|
210
|
+
}
|