plass-ui 0.0.1

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.
@@ -0,0 +1,498 @@
1
+ /**
2
+ * Plass design tokens.
3
+ *
4
+ * This file ships as `plass-ui/tailwind.css`, for a project that runs Tailwind
5
+ * itself and wants the components' utilities generated in the same pass as its
6
+ * own:
7
+ *
8
+ * @import 'tailwindcss';
9
+ * @import 'plass-ui/tailwind.css';
10
+ *
11
+ * Two lines, and no `@source` of their own — the `@source` below does that job.
12
+ *
13
+ * A project with no Tailwind imports `plass-ui/styles.css` instead, which is this
14
+ * file plus a reset plus the compiled utilities, built by
15
+ * `scripts/build-styles.mjs` from `src/standalone.css`. The tokens below are
16
+ * the same in both.
17
+ *
18
+ * Components never hardcode a colour; they map a `color` prop onto the `--p-*`
19
+ * local slots and let the cascade resolve the rest.
20
+ *
21
+ * Dark mode responds to `prefers-color-scheme`, and can be forced either way
22
+ * with `.dark` / `[data-theme='dark'|'light']` on any ancestor.
23
+ *
24
+ * The surface model is **a moulded plastic key resting on a sheet of glass**: a
25
+ * control is a gradient fill with a specular highlight along its top edge and a
26
+ * drop shadow tinted with its own colour; everything that holds content is a
27
+ * translucent, heavily blurred sheet with a white hairline around it.
28
+ */
29
+
30
+ /*
31
+ * The components' classes are Tailwind utilities, so Tailwind has to see the
32
+ * files that spell them out — otherwise every component renders unstyled.
33
+ *
34
+ * `@source` resolves relative to the file it is written in, not to whatever
35
+ * imported it. In a consumer's install that is `node_modules/plass-ui/dist/`, next
36
+ * to the compiled components; in this repository it is `src/`, next to the
37
+ * `.tsx`. One line covers both, and an explicitly registered source is scanned
38
+ * even inside `node_modules`, which automatic detection skips.
39
+ *
40
+ * This is why consumers do not need an `@source '../node_modules/plass-ui'` of
41
+ * their own — a path that would depend on where their CSS file happens to sit.
42
+ */
43
+ @source '.';
44
+
45
+ /* ---------------------------------------------------------------------------
46
+ * The colour families
47
+ *
48
+ * These sit outside every theme block on purpose, and it is the one place Plass
49
+ * departs hardest from the libraries it was learned from: **a key's own colour
50
+ * does not change with the theme.** A piece of plastic is the same piece of
51
+ * plastic in a dark room. What changes is the sheet it rests on, the glass
52
+ * around it, and the `accent` — the one value that has to be *read* off a
53
+ * surface rather than looked at.
54
+ *
55
+ * Two values are hand-picked per family and everything else is derived with
56
+ * `color-mix()`, so **adding a colour family is two edits**: one entry in
57
+ * `PlassColor` and two lines here (plus its `accent` in each theme).
58
+ *
59
+ * Every `solid` was chosen against one number: the *lightest* stop of the
60
+ * gradient derived from it has to clear 4.5:1 against its own `on-solid`. That
61
+ * stop is a 5% lift toward white, which is why the fills sit where they do —
62
+ * they are not "dark blue", they are as bright as a white label allows.
63
+ * ------------------------------------------------------------------------- */
64
+
65
+ :root {
66
+ --plass-primary-solid: #3558ef;
67
+ --plass-primary-on-solid: #ffffff;
68
+
69
+ --plass-secondary-solid: #4f5a70;
70
+ --plass-secondary-on-solid: #ffffff;
71
+
72
+ --plass-success-solid: #157845;
73
+ --plass-success-on-solid: #ffffff;
74
+
75
+ /* The one family with dark ink. White on amber does not reach 4.5:1 at any
76
+ lightness worth calling amber, and changing the ink is the right answer —
77
+ distorting the family to preserve a white label is not. */
78
+ --plass-warning-solid: #e2921c;
79
+ --plass-warning-on-solid: #3b2405;
80
+
81
+ --plass-danger-solid: #c4383e;
82
+ --plass-danger-on-solid: #ffffff;
83
+
84
+ --plass-info-solid: #186cb3;
85
+ --plass-info-on-solid: #ffffff;
86
+ }
87
+
88
+ /* ---------------------------------------------------------------------------
89
+ * Light theme (default)
90
+ *
91
+ * `.light` and `[data-theme='light']` carry the same block rather than only
92
+ * `:root`, because forcing light has to work on a nested element too — a
93
+ * preview box inside a dark page has nowhere else to get these values from.
94
+ * Everything here that is not a colour (the radii, the motion) is identical in
95
+ * both themes, so re-declaring it costs a duplicate that resolves to what was
96
+ * inherited anyway.
97
+ * ------------------------------------------------------------------------- */
98
+
99
+ :root,
100
+ .light,
101
+ [data-theme='light'] {
102
+ /* Neutral surfaces and content */
103
+ --plass-surface: #ffffff;
104
+ --plass-fg: #1b1e27;
105
+ --plass-muted-fg: #5b6376;
106
+ --plass-border: #d5deef;
107
+
108
+ /* The page a Plass screen is laid on. Not used by any component — a library
109
+ has no business painting the body — but it is what the docs, the demos and
110
+ the focus ring's offset are drawn against, and it is the backdrop the glass
111
+ was tuned over. */
112
+ --plass-bg-from: #eef2fb;
113
+ --plass-bg-to: #dfe8f8;
114
+
115
+ /* Glass ---------------------------------------------------------------
116
+
117
+ Three strengths of one translucent sheet, and the ladder is **opacity
118
+ rather than lightness**: as a surface is engaged it holds more light,
119
+ instead of turning grey.
120
+
121
+ Undyed on purpose. A sheet holds other people's content, which arrives
122
+ with its own colours; tinting it puts every one of them on a background
123
+ they were not chosen against. The family shows up in the hairline, the
124
+ focus ring and the caret, and the glass stays clear. */
125
+ --plass-glass: rgb(255 255 255 / 0.62);
126
+ --plass-glass-hover: rgb(255 255 255 / 0.76);
127
+ --plass-glass-press: rgb(255 255 255 / 0.88);
128
+ --plass-glass-line: rgb(255 255 255 / 0.6);
129
+
130
+ /* How hard the backdrop is blurred, and how much colour is pulled out of it.
131
+ 22px is a deliberate, generous smear: unlike a frosted-acrylic language,
132
+ Plass is not trying to let you read what is behind the sheet — it is trying
133
+ to make the sheet look thick. Below about 14px the glass reads as a flat
134
+ white box with an alpha on it. */
135
+ --plass-blur: blur(22px) saturate(160%);
136
+
137
+ /* The two edges. `gloss-glass` is the light lying along the top of a sheet;
138
+ `gloss-solid` is the same light on a moulded key, which is dimmer because
139
+ the key is not clear. There is deliberately **no dark bevel underneath**:
140
+ the drop shadow is what puts the key above the sheet. */
141
+ --plass-gloss-glass: inset 0 1px 0 rgb(255 255 255 / 0.55);
142
+ --plass-gloss-solid: inset 0 1px 0 rgb(255 255 255 / 0.34);
143
+
144
+ /* A well cut into the glass — what a filled field is drawn as. The only
145
+ inset shadow in the library that goes *downward*. */
146
+ --plass-well: inset 0 1px 2px rgb(20 40 90 / 0.1);
147
+
148
+ /* The specular highlight `.plass-gloss` lays over a key. It stops at 62%
149
+ rather than running the whole height, because a gradient that reaches the
150
+ bottom edge reads as a colour change and not as light. */
151
+ --plass-specular: linear-gradient(
152
+ 180deg,
153
+ rgb(255 255 255 / 0.3) 0%,
154
+ rgb(255 255 255 / 0.06) 46%,
155
+ rgb(255 255 255 / 0) 62%
156
+ );
157
+
158
+ /* Elevation, and how strongly a key's own colour bleeds into the shadow it
159
+ casts. The ambient is a blue-grey rather than black: a neutral shadow over
160
+ a blue-tinted page reads as dirt. */
161
+ --plass-shadow-ambient: rgb(20 40 90 / 0.16);
162
+ --plass-glow-strength: 35%;
163
+
164
+ /* The scrim behind a modal surface. */
165
+ --plass-scrim: rgb(16 24 40 / 0.4);
166
+
167
+ /* Corner radius: roughly 30% of the control height, and roughly constant in
168
+ feel across the ladder — a Plass corner is a moulded fillet, not a pill and
169
+ not a chamfer. */
170
+ --plass-radius-xs: 0.5rem;
171
+ --plass-radius-sm: 0.625rem;
172
+ --plass-radius-md: 0.75rem;
173
+ --plass-radius-lg: 0.875rem;
174
+ --plass-radius-xl: 1rem;
175
+
176
+ /* Motion. One duration, one curve, and the same both ways — a key going down
177
+ and a key coming back up are the same spring. */
178
+ --plass-ease: cubic-bezier(0.16, 0.9, 0.3, 1);
179
+ --plass-duration: 150ms;
180
+ --plass-duration-slow: 260ms;
181
+
182
+ /* Readable on a surface. This is the one family value that is per-theme,
183
+ because it is the one that has to be read rather than looked at. */
184
+ --plass-primary-accent: #2c49d6;
185
+ --plass-secondary-accent: #46506a;
186
+ --plass-success-accent: #146b41;
187
+ --plass-warning-accent: #96600c;
188
+ --plass-danger-accent: #bb3339;
189
+ --plass-info-accent: #1668ae;
190
+ }
191
+
192
+ /* ---------------------------------------------------------------------------
193
+ * Dark theme
194
+ *
195
+ * The fills do not move — see the note above the families. What moves is the
196
+ * ground under them: the glass loses its white and becomes a smoked pane, the
197
+ * shadow goes black and deepens, and the accents lighten far enough to be read
198
+ * off the dark sheet.
199
+ * ------------------------------------------------------------------------- */
200
+
201
+ @media (prefers-color-scheme: dark) {
202
+ :root:not(.light):not([data-theme='light']) {
203
+ --plass-surface: #141b30;
204
+ --plass-fg: #e7eaf3;
205
+ --plass-muted-fg: #99a2ba;
206
+ --plass-border: rgb(255 255 255 / 0.12);
207
+
208
+ --plass-bg-from: #111731;
209
+ --plass-bg-to: #0a0e1c;
210
+
211
+ --plass-glass: rgb(255 255 255 / 0.07);
212
+ --plass-glass-hover: rgb(255 255 255 / 0.11);
213
+ --plass-glass-press: rgb(255 255 255 / 0.15);
214
+ --plass-glass-line: rgb(255 255 255 / 0.12);
215
+
216
+ --plass-blur: blur(22px) saturate(150%);
217
+
218
+ --plass-gloss-glass: inset 0 1px 0 rgb(255 255 255 / 0.1);
219
+ --plass-gloss-solid: inset 0 1px 0 rgb(255 255 255 / 0.22);
220
+
221
+ --plass-well: inset 0 1px 2px rgb(0 0 0 / 0.45);
222
+
223
+ --plass-specular: linear-gradient(
224
+ 180deg,
225
+ rgb(255 255 255 / 0.18) 0%,
226
+ rgb(255 255 255 / 0.04) 46%,
227
+ rgb(255 255 255 / 0) 62%
228
+ );
229
+
230
+ /* A tinted shadow has almost nothing to sit on over a near-black page, so
231
+ the tint is turned up rather than the shadow being made bigger. */
232
+ --plass-shadow-ambient: rgb(0 0 0 / 0.5);
233
+ --plass-glow-strength: 55%;
234
+
235
+ --plass-scrim: rgb(0 0 0 / 0.6);
236
+
237
+ --plass-primary-accent: #93a8ff;
238
+ --plass-secondary-accent: #aab3c6;
239
+ --plass-success-accent: #4fd08d;
240
+ --plass-warning-accent: #f2b74e;
241
+ --plass-danger-accent: #ff8a8f;
242
+ --plass-info-accent: #5fb4ee;
243
+ }
244
+ }
245
+
246
+ .dark,
247
+ [data-theme='dark'] {
248
+ --plass-surface: #141b30;
249
+ --plass-fg: #e7eaf3;
250
+ --plass-muted-fg: #99a2ba;
251
+ --plass-border: rgb(255 255 255 / 0.12);
252
+
253
+ --plass-bg-from: #111731;
254
+ --plass-bg-to: #0a0e1c;
255
+
256
+ --plass-glass: rgb(255 255 255 / 0.07);
257
+ --plass-glass-hover: rgb(255 255 255 / 0.11);
258
+ --plass-glass-press: rgb(255 255 255 / 0.15);
259
+ --plass-glass-line: rgb(255 255 255 / 0.12);
260
+
261
+ --plass-blur: blur(22px) saturate(150%);
262
+
263
+ --plass-gloss-glass: inset 0 1px 0 rgb(255 255 255 / 0.1);
264
+ --plass-gloss-solid: inset 0 1px 0 rgb(255 255 255 / 0.22);
265
+
266
+ --plass-well: inset 0 1px 2px rgb(0 0 0 / 0.45);
267
+
268
+ --plass-specular: linear-gradient(
269
+ 180deg,
270
+ rgb(255 255 255 / 0.18) 0%,
271
+ rgb(255 255 255 / 0.04) 46%,
272
+ rgb(255 255 255 / 0) 62%
273
+ );
274
+
275
+ --plass-shadow-ambient: rgb(0 0 0 / 0.5);
276
+ --plass-glow-strength: 55%;
277
+
278
+ --plass-scrim: rgb(0 0 0 / 0.6);
279
+
280
+ --plass-primary-accent: #93a8ff;
281
+ --plass-secondary-accent: #aab3c6;
282
+ --plass-success-accent: #4fd08d;
283
+ --plass-warning-accent: #f2b74e;
284
+ --plass-danger-accent: #ff8a8f;
285
+ --plass-info-accent: #5fb4ee;
286
+ }
287
+
288
+ /* ---------------------------------------------------------------------------
289
+ * Derived tokens
290
+ *
291
+ * Gradients, glows, tints, hairlines and focus rings are all one base colour at
292
+ * different strengths, so they are computed rather than hand-picked per theme.
293
+ *
294
+ * **This block is repeated for every theme root**, because a custom property
295
+ * resolves its `var()`s on the element that declares it — declaring these only
296
+ * on `:root` would freeze `--plass-*-glow` to the light theme's
297
+ * `--plass-glow-strength` inside a `.dark` subtree.
298
+ * ------------------------------------------------------------------------- */
299
+
300
+ :root,
301
+ .dark,
302
+ .light,
303
+ [data-theme='dark'],
304
+ [data-theme='light'] {
305
+ /* The elevation ladder. Neutral, and it climbs by *blur* far more than by
306
+ offset: a key that moves 20px down the page when it is raised has left the
307
+ sheet, and everything in this library is still sitting on one. */
308
+ --plass-shadow-0: 0 0 #0000;
309
+ --plass-shadow-1: 0 2px 8px -2px var(--plass-shadow-ambient);
310
+ --plass-shadow-2: 0 6px 20px -4px var(--plass-shadow-ambient);
311
+ --plass-shadow-3: 0 12px 32px -8px var(--plass-shadow-ambient);
312
+ --plass-shadow-4: 0 20px 48px -12px var(--plass-shadow-ambient);
313
+
314
+ /* The moulded fill. Three stops rather than two: light lands on the top-left
315
+ corner, the family's own colour holds the middle where the label sits, and
316
+ the far corner falls away. 135deg is the light direction the whole library
317
+ agrees on — the specular highlight, the gloss line and this all assume it.
318
+
319
+ The lift is 5% and the fall is 10%, which is smaller than it looks like it
320
+ should be. Anything more and the top-left corner stops clearing 4.5:1
321
+ against the label. */
322
+ --plass-primary-fill: linear-gradient(
323
+ 135deg,
324
+ color-mix(in srgb, var(--plass-primary-solid) 95%, white) 0%,
325
+ var(--plass-primary-solid) 46%,
326
+ color-mix(in srgb, var(--plass-primary-solid) 90%, black) 100%
327
+ );
328
+ --plass-primary-glow: color-mix(
329
+ in srgb,
330
+ var(--plass-primary-solid) var(--plass-glow-strength),
331
+ transparent
332
+ );
333
+ --plass-primary-soft: color-mix(in srgb, var(--plass-primary-accent) 10%, transparent);
334
+ --plass-primary-soft-hover: color-mix(in srgb, var(--plass-primary-accent) 18%, transparent);
335
+ --plass-primary-soft-press: color-mix(in srgb, var(--plass-primary-accent) 26%, transparent);
336
+ --plass-primary-line: color-mix(in srgb, var(--plass-primary-accent) 30%, transparent);
337
+ --plass-primary-line-hover: color-mix(in srgb, var(--plass-primary-accent) 48%, transparent);
338
+ --plass-primary-ring: color-mix(in srgb, var(--plass-primary-solid) 55%, transparent);
339
+
340
+ --plass-secondary-fill: linear-gradient(
341
+ 135deg,
342
+ color-mix(in srgb, var(--plass-secondary-solid) 95%, white) 0%,
343
+ var(--plass-secondary-solid) 46%,
344
+ color-mix(in srgb, var(--plass-secondary-solid) 90%, black) 100%
345
+ );
346
+ --plass-secondary-glow: color-mix(
347
+ in srgb,
348
+ var(--plass-secondary-solid) var(--plass-glow-strength),
349
+ transparent
350
+ );
351
+ --plass-secondary-soft: color-mix(in srgb, var(--plass-secondary-accent) 10%, transparent);
352
+ --plass-secondary-soft-hover: color-mix(in srgb, var(--plass-secondary-accent) 18%, transparent);
353
+ --plass-secondary-soft-press: color-mix(in srgb, var(--plass-secondary-accent) 26%, transparent);
354
+ --plass-secondary-line: color-mix(in srgb, var(--plass-secondary-accent) 30%, transparent);
355
+ --plass-secondary-line-hover: color-mix(in srgb, var(--plass-secondary-accent) 48%, transparent);
356
+ --plass-secondary-ring: color-mix(in srgb, var(--plass-secondary-solid) 55%, transparent);
357
+
358
+ --plass-success-fill: linear-gradient(
359
+ 135deg,
360
+ color-mix(in srgb, var(--plass-success-solid) 95%, white) 0%,
361
+ var(--plass-success-solid) 46%,
362
+ color-mix(in srgb, var(--plass-success-solid) 90%, black) 100%
363
+ );
364
+ --plass-success-glow: color-mix(
365
+ in srgb,
366
+ var(--plass-success-solid) var(--plass-glow-strength),
367
+ transparent
368
+ );
369
+ --plass-success-soft: color-mix(in srgb, var(--plass-success-accent) 10%, transparent);
370
+ --plass-success-soft-hover: color-mix(in srgb, var(--plass-success-accent) 18%, transparent);
371
+ --plass-success-soft-press: color-mix(in srgb, var(--plass-success-accent) 26%, transparent);
372
+ --plass-success-line: color-mix(in srgb, var(--plass-success-accent) 30%, transparent);
373
+ --plass-success-line-hover: color-mix(in srgb, var(--plass-success-accent) 48%, transparent);
374
+ --plass-success-ring: color-mix(in srgb, var(--plass-success-solid) 55%, transparent);
375
+
376
+ --plass-warning-fill: linear-gradient(
377
+ 135deg,
378
+ color-mix(in srgb, var(--plass-warning-solid) 95%, white) 0%,
379
+ var(--plass-warning-solid) 46%,
380
+ color-mix(in srgb, var(--plass-warning-solid) 90%, black) 100%
381
+ );
382
+ --plass-warning-glow: color-mix(
383
+ in srgb,
384
+ var(--plass-warning-solid) var(--plass-glow-strength),
385
+ transparent
386
+ );
387
+ --plass-warning-soft: color-mix(in srgb, var(--plass-warning-accent) 12%, transparent);
388
+ --plass-warning-soft-hover: color-mix(in srgb, var(--plass-warning-accent) 20%, transparent);
389
+ --plass-warning-soft-press: color-mix(in srgb, var(--plass-warning-accent) 28%, transparent);
390
+ --plass-warning-line: color-mix(in srgb, var(--plass-warning-accent) 34%, transparent);
391
+ --plass-warning-line-hover: color-mix(in srgb, var(--plass-warning-accent) 52%, transparent);
392
+ --plass-warning-ring: color-mix(in srgb, var(--plass-warning-solid) 55%, transparent);
393
+
394
+ --plass-danger-fill: linear-gradient(
395
+ 135deg,
396
+ color-mix(in srgb, var(--plass-danger-solid) 95%, white) 0%,
397
+ var(--plass-danger-solid) 46%,
398
+ color-mix(in srgb, var(--plass-danger-solid) 90%, black) 100%
399
+ );
400
+ --plass-danger-glow: color-mix(
401
+ in srgb,
402
+ var(--plass-danger-solid) var(--plass-glow-strength),
403
+ transparent
404
+ );
405
+ --plass-danger-soft: color-mix(in srgb, var(--plass-danger-accent) 10%, transparent);
406
+ --plass-danger-soft-hover: color-mix(in srgb, var(--plass-danger-accent) 18%, transparent);
407
+ --plass-danger-soft-press: color-mix(in srgb, var(--plass-danger-accent) 26%, transparent);
408
+ --plass-danger-line: color-mix(in srgb, var(--plass-danger-accent) 30%, transparent);
409
+ --plass-danger-line-hover: color-mix(in srgb, var(--plass-danger-accent) 48%, transparent);
410
+ --plass-danger-ring: color-mix(in srgb, var(--plass-danger-solid) 55%, transparent);
411
+
412
+ --plass-info-fill: linear-gradient(
413
+ 135deg,
414
+ color-mix(in srgb, var(--plass-info-solid) 95%, white) 0%,
415
+ var(--plass-info-solid) 46%,
416
+ color-mix(in srgb, var(--plass-info-solid) 90%, black) 100%
417
+ );
418
+ --plass-info-glow: color-mix(
419
+ in srgb,
420
+ var(--plass-info-solid) var(--plass-glow-strength),
421
+ transparent
422
+ );
423
+ --plass-info-soft: color-mix(in srgb, var(--plass-info-accent) 10%, transparent);
424
+ --plass-info-soft-hover: color-mix(in srgb, var(--plass-info-accent) 18%, transparent);
425
+ --plass-info-soft-press: color-mix(in srgb, var(--plass-info-accent) 26%, transparent);
426
+ --plass-info-line: color-mix(in srgb, var(--plass-info-accent) 30%, transparent);
427
+ --plass-info-line-hover: color-mix(in srgb, var(--plass-info-accent) 48%, transparent);
428
+ --plass-info-ring: color-mix(in srgb, var(--plass-info-solid) 55%, transparent);
429
+ }
430
+
431
+ /* ---------------------------------------------------------------------------
432
+ * Sheet titles
433
+ *
434
+ * A `title` is styled by the element the component renders around it, so a
435
+ * plain string needs nothing. But a sheet that belongs in the document outline
436
+ * has to be given a real heading, `title={<h2>…</h2>}`, and that heading
437
+ * arrives with the browser's own 1.5em bold and its margins, which would ignore
438
+ * the type scale just set.
439
+ *
440
+ * This is in CSS rather than in Tailwind because the selector is the point:
441
+ * `[&_:where(h1,h2,h3,h4,h5,h6)]:[font:inherit]` is expressible and nobody
442
+ * would want to read it twice.
443
+ * ------------------------------------------------------------------------- */
444
+
445
+ .plass-title :where(h1, h2, h3, h4, h5, h6) {
446
+ font: inherit;
447
+ margin: 0;
448
+ }
449
+
450
+ /* ---------------------------------------------------------------------------
451
+ * The gloss
452
+ *
453
+ * One layer over a moulded surface: the specular highlight that says the light
454
+ * is coming from the top-left and that the thing under it is not flat.
455
+ *
456
+ * It is **the material, not a state** — it is there when nothing is happening,
457
+ * which is exactly what makes it plastic rather than an effect. The only thing
458
+ * that moves it is a press: the key tips away from the light and the highlight
459
+ * drops out, which is the whole of the press signature together with the
460
+ * shadow contracting under it.
461
+ *
462
+ * This lives in CSS rather than in Tailwind arbitrary variants because the
463
+ * selector is a pseudo-element carrying a gradient: `[&::before]:[background:
464
+ * linear-gradient(...)]` is technically expressible and completely unreadable.
465
+ *
466
+ * Cost per control: one composited layer that never repaints. There is no
467
+ * pointer tracking, no ripple element, no timer and no JavaScript.
468
+ * ------------------------------------------------------------------------- */
469
+
470
+ .plass-gloss::before {
471
+ content: '';
472
+ position: absolute;
473
+ inset: 0;
474
+ border-radius: inherit;
475
+ pointer-events: none;
476
+ background-image: var(--plass-specular);
477
+ opacity: 1;
478
+ transition: opacity var(--plass-duration) var(--plass-ease);
479
+ }
480
+
481
+ .plass-gloss:active::before {
482
+ opacity: 0.3;
483
+ }
484
+
485
+ /* ---------------------------------------------------------------------------
486
+ * Reduced motion
487
+ *
488
+ * Nothing in Plass animates on its own except a spinner, so this is about the
489
+ * transitions: someone who has asked for less movement is asking for state
490
+ * changes to *arrive*, not to be taken away. The colours still change; they
491
+ * simply stop easing.
492
+ * ------------------------------------------------------------------------- */
493
+
494
+ @media (prefers-reduced-motion: reduce) {
495
+ .plass-gloss::before {
496
+ transition-duration: 1ms;
497
+ }
498
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Shared prop vocabulary for every Plass component.
3
+ *
4
+ * These names and values are deliberately generic: a `size` of `md` or a
5
+ * `color` of `primary` has to mean the same thing on a Button, a TextField, a
6
+ * Card or a Dialog. Components pick the subset they need from here and never
7
+ * invent a parallel spelling of the same idea.
8
+ */
9
+ /** Scale of a component. `md` is the desktop default. */
10
+ export type PlassSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
+ /** Semantic color role. Maps to a token family in `styles.css`. */
12
+ export type PlassColor = 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info';
13
+ /**
14
+ * How tightly a component packs its content. Only spacing changes — never the
15
+ * type scale or the control's own height — so a compact and a default control
16
+ * of the same `size` still line up on a shared baseline.
17
+ */
18
+ export type PlassDensity = 'default' | 'compact';
19
+ /**
20
+ * Which way a component runs. `horizontal` everywhere it is offered, because a
21
+ * vertical control is the exception and an exception should have to be asked
22
+ * for.
23
+ */
24
+ export type PlassOrientation = 'horizontal' | 'vertical';
25
+ /**
26
+ * Which edge of an anchor something is placed against.
27
+ *
28
+ * Physical rather than logical — `start`/`end` would be wrong here, because a
29
+ * tooltip above a button is above it in every writing direction.
30
+ */
31
+ export type PlassSide = 'top' | 'right' | 'bottom' | 'left';
32
+ /**
33
+ * Where something sits along the axis it is not placed on.
34
+ *
35
+ * `start`/`end` rather than `left`/`right` because these flip under RTL, which
36
+ * is the whole reason the library never says `left`.
37
+ */
38
+ export type PlassAlign = 'start' | 'center' | 'end';
39
+ /**
40
+ * What a surface is made of. This is the library's own name, and the two
41
+ * materials in it are the whole design language.
42
+ *
43
+ * - `solid` — **plastic.** A moulded key: a gradient fill, a specular highlight
44
+ * along its top edge, and a drop shadow tinted with its own colour. One per
45
+ * view, for the action the screen is about.
46
+ * - `glass` — **glass.** A translucent sheet over a blurred backdrop with a
47
+ * white hairline around it. Secondary actions, and the default for anything
48
+ * that *holds* content rather than being pressed.
49
+ * - `ghost` — neither. No surface at all until the pointer is on it.
50
+ * Tertiary and inline actions.
51
+ */
52
+ export type PlassVariant = 'solid' | 'glass' | 'ghost';
53
+ /**
54
+ * How far a surface sits off the page, as a drop shadow.
55
+ *
56
+ * A plastic key rests **on** the sheet rather than flush with it, so a Button
57
+ * defaults to `1` and not to `0`. Hovering adds a level and pressing removes
58
+ * one, which is what puts the key down against the glass under the finger.
59
+ *
60
+ * `0` is flat, and it is the right default for anything a reader looks *into*
61
+ * rather than presses — a field, a well, a panel behind other content.
62
+ */
63
+ export type PlassElevation = 0 | 1 | 2 | 3;
64
+ /** Style props shared by most components; spread into their own prop types. */
65
+ export interface PlassStyleProps {
66
+ /** @default 'solid' */
67
+ variant?: PlassVariant;
68
+ /** @default 'md' */
69
+ size?: PlassSize;
70
+ /** @default 'primary' */
71
+ color?: PlassColor;
72
+ /** @default 'default' */
73
+ density?: PlassDensity;
74
+ }
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Shared prop vocabulary for every Plass component.
3
+ *
4
+ * These names and values are deliberately generic: a `size` of `md` or a
5
+ * `color` of `primary` has to mean the same thing on a Button, a TextField, a
6
+ * Card or a Dialog. Components pick the subset they need from here and never
7
+ * invent a parallel spelling of the same idea.
8
+ */