@blakesteve/roster 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -2
- package/dist/components/atoms/Input/Input.d.ts +10 -1
- package/dist/components/atoms/Input/input-variants.d.ts +1 -0
- package/dist/components/atoms/Select/Select.d.ts +11 -1
- package/dist/components/atoms/Select/select-variants.d.ts +4 -0
- package/dist/components/atoms/Textarea/Textarea.d.ts +9 -0
- package/dist/meta.json +2 -2
- package/dist/roster.cjs.js +2 -2
- package/dist/roster.css +1 -1
- package/dist/roster.es.js +815 -650
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,6 +144,14 @@ document head:
|
|
|
144
144
|
If you pass a custom `storageKey`, change the script to match — a mismatch means
|
|
145
145
|
the script and the toggle disagree, which shows up as a flash on every reload.
|
|
146
146
|
|
|
147
|
+
**Components that render into a portal**, such as `Select`'s menu, are attached
|
|
148
|
+
to `<body>` rather than to the element you wrote them next to. When `.dark` is
|
|
149
|
+
on `<html>` — what `ThemeToggle` does, and what the script above sets up — that
|
|
150
|
+
makes no difference, because the portal is still a descendant. If you scope
|
|
151
|
+
`.dark` to a subtree instead, `Select` detects the nearest `.dark` ancestor of
|
|
152
|
+
the field and carries it across the portal, so the menu follows the same scope
|
|
153
|
+
the trigger is in.
|
|
154
|
+
|
|
147
155
|
`Navbar`'s `themeMode` prop is a different thing: it describes what palette the
|
|
148
156
|
bar paints _itself_ with. Pair them with `themeMode="auto"` and the nav follows
|
|
149
157
|
whatever `ThemeToggle` sets.
|
|
@@ -349,6 +357,99 @@ browser honors `scrollbar-width` / `scrollbar-color`, and drops the WebKit
|
|
|
349
357
|
pseudo-elements for any element that sets either of them, so on anything modern
|
|
350
358
|
`thin` is the whole shape control available.
|
|
351
359
|
|
|
360
|
+
### Control surface colors
|
|
361
|
+
|
|
362
|
+
`Input`, `PasswordInput` and `Select` read these variables in their `outline`
|
|
363
|
+
variant, so a consumer can repaint a form control without restating the class
|
|
364
|
+
list.
|
|
365
|
+
|
|
366
|
+
```css
|
|
367
|
+
:root {
|
|
368
|
+
--roster-control-border: #d6d3d1;
|
|
369
|
+
--roster-control-bg: transparent;
|
|
370
|
+
--roster-control-text: #1c1917;
|
|
371
|
+
--roster-control-border-focus: #0f6498;
|
|
372
|
+
}
|
|
373
|
+
.dark {
|
|
374
|
+
--roster-control-border: #44403c;
|
|
375
|
+
--roster-control-bg: transparent;
|
|
376
|
+
--roster-control-text: #f5f5f4;
|
|
377
|
+
--roster-control-border-focus: #0f6498;
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Set them in both scopes, for the same reason the scrollbar thumb needs both:
|
|
382
|
+
Roster's own `.dark` rule has equal specificity and comes later.
|
|
383
|
+
|
|
384
|
+
The defaults are the values the component used to hardcode, so installing this
|
|
385
|
+
version changes nothing on its own. Placeholder color, the error state, and the
|
|
386
|
+
focus ring are deliberately not on tokens — the ring is already
|
|
387
|
+
`--roster-ring`, and error styling should stay recognizably an error.
|
|
388
|
+
|
|
389
|
+
Not every control uses every token: `Select` takes the first three, because
|
|
390
|
+
focus on its trigger is the shared `--roster-ring` rather than a border color.
|
|
391
|
+
One family rather than one per component is deliberate — these controls sit in
|
|
392
|
+
the same row of the same form and are drawn to look identical, so being able to
|
|
393
|
+
repaint one and not the other would be a bug, not a choice.
|
|
394
|
+
|
|
395
|
+
The `outline` variant is the only one that reads them, in every component, and
|
|
396
|
+
it carries no hover fill. The other variants each name a concrete surface
|
|
397
|
+
(`white`, `soft`, `slate`, `ghost`) and stay opinionated: a token that meant
|
|
398
|
+
something different in each would not be a token.
|
|
399
|
+
|
|
400
|
+
One caveat worth knowing before you lean on these: like `--roster-card-*`,
|
|
401
|
+
`--roster-nav-*` and `--roster-footer-*`, they are declared outside
|
|
402
|
+
`@layer roster` and resolved at `:root`. A host that declares its override
|
|
403
|
+
inside `@layer base` is outranked, and an override scoped to a subtree rather
|
|
404
|
+
than the document root will not reach them.
|
|
405
|
+
|
|
406
|
+
### Reaching the control
|
|
407
|
+
|
|
408
|
+
`className` on a field component lands on the outer `Field` wrapper. That is
|
|
409
|
+
right for layout — width, flex, margins — and useless for anything else: it
|
|
410
|
+
cannot reach the element that draws the border, the height or the font.
|
|
411
|
+
|
|
412
|
+
Every field therefore has a second prop, **named for the element it reaches**:
|
|
413
|
+
|
|
414
|
+
| Component | Wrapper | The control itself |
|
|
415
|
+
| -------------- | ----------- | -------------------- |
|
|
416
|
+
| `Input` | `className` | `inputClassName` |
|
|
417
|
+
| `PasswordInput`| `className` | `inputClassName` |
|
|
418
|
+
| `Select` | `className` | `triggerClassName` |
|
|
419
|
+
| `Textarea` | `className` | `textareaClassName` |
|
|
420
|
+
|
|
421
|
+
The names differ on purpose. A single `controlClassName` would name a concept
|
|
422
|
+
rather than an element, and it breaks the first time a component has two
|
|
423
|
+
styleable inner parts — a combobox has an input *and* a toggle. `triggerClassName`
|
|
424
|
+
also tells you something true: the thing you are styling is a `<button>`, so
|
|
425
|
+
`placeholder:` and `disabled:` behave differently on it than they would on an
|
|
426
|
+
`<input>`.
|
|
427
|
+
|
|
428
|
+
These merge last and win, not because they come later in the attribute (CSS
|
|
429
|
+
ignores that) but because `cn` is tailwind-merge and deletes the class they
|
|
430
|
+
conflict with.
|
|
431
|
+
|
|
432
|
+
### Control heights
|
|
433
|
+
|
|
434
|
+
`Button`, `Input` and `Select` share one size scale, so they line up when set
|
|
435
|
+
side by side in a row:
|
|
436
|
+
|
|
437
|
+
| `size` | Height | `Button` padding | `Input` padding | `Select` padding |
|
|
438
|
+
| --------- | ------ | ---------------- | --------------- | ---------------- |
|
|
439
|
+
| `sm` | 36px | `px-3` | `px-3` | `pl-3 pr-9` |
|
|
440
|
+
| `default` | 40px | `px-4` | `px-4` | `pl-4 pr-10` |
|
|
441
|
+
| `lg` | 44px | `px-8` | `px-4` | `pl-4 pr-10` |
|
|
442
|
+
|
|
443
|
+
Padding diverges at `lg` on purpose. A button's label is centered and wants the
|
|
444
|
+
room, while a field's text is left-aligned and a wide inset only pushes the
|
|
445
|
+
caret toward the middle. `Select`'s right padding is clearance for the chevron,
|
|
446
|
+
which is a fixed 14px at every size, so it has nothing to grow for.
|
|
447
|
+
|
|
448
|
+
Unit tests assert the three components resolve to the same height at every
|
|
449
|
+
shared size, so they cannot drift apart. `Button` also has `xs` and `icon`,
|
|
450
|
+
which neither field has an equivalent for. `Textarea` is not on this scale — it
|
|
451
|
+
is multi-line, so the answer there is a min-height rather than a height.
|
|
452
|
+
|
|
352
453
|
### Components that render links
|
|
353
454
|
|
|
354
455
|
`Breadcrumbs` renders a plain `<a>` by default, which is right for a static
|
|
@@ -408,7 +509,7 @@ function App() {
|
|
|
408
509
|
| `Disclosure` | Show/hide toggle using HeadlessUI |
|
|
409
510
|
| `Eyebrow` | Small tracked-out uppercase label above a heading or beside a rule; polymorphic via `as` |
|
|
410
511
|
| `InlineCode` | Inline `<code>` for identifiers in running prose |
|
|
411
|
-
| `Input` | Text input with label, error state, and
|
|
512
|
+
| `Input` | Text input with label, error state, icon slots, and a size scale matching `Button` |
|
|
412
513
|
| `LabeledDivider` | Horizontal rule carrying a label, with an optional trailing count |
|
|
413
514
|
| `Link` | Styled anchor with variant support |
|
|
414
515
|
| `PasswordInput` | Password field with a show/hide reveal toggle |
|
|
@@ -416,7 +517,7 @@ function App() {
|
|
|
416
517
|
| `AvatarStrip` | Stacked avatar row with overflow chip, dismiss button, trailing slot, and label area |
|
|
417
518
|
| `CollapsibleSection` | Clamps any content (prose, chips, image grids) to a fixed height with a fade and expand/collapse toggle |
|
|
418
519
|
| `LiquidTabs` | Controlled tab strip with a liquid sliding pill indicator: pill and filled variants |
|
|
419
|
-
| `Select` | Dropdown selector
|
|
520
|
+
| `Select` | Dropdown selector, on the same size scale as `Button` and `Input` |
|
|
420
521
|
| `SegmentBar` | Proportional horizontal bar divided into colored segments with optional legend |
|
|
421
522
|
| `Stat` | A single figure with its label and, optionally, where the figure came from |
|
|
422
523
|
| `Spinner` | Loading indicator |
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
3
|
import { inputVariants } from './input-variants';
|
|
4
|
-
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>, VariantProps<typeof inputVariants> {
|
|
4
|
+
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">, VariantProps<typeof inputVariants> {
|
|
5
5
|
label?: string;
|
|
6
6
|
helperText?: string;
|
|
7
7
|
errorMessage?: string;
|
|
8
8
|
startIcon?: React.ReactNode;
|
|
9
9
|
endIcon?: React.ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* Classes for the `<input>` itself.
|
|
12
|
+
*
|
|
13
|
+
* `className` lands on the outer `Field` wrapper, which is right for layout
|
|
14
|
+
* and useless for anything else: a consumer could not reach the control to
|
|
15
|
+
* change its height, border or background. `size` covers the common case;
|
|
16
|
+
* this is the escape hatch for the rest.
|
|
17
|
+
*/
|
|
18
|
+
inputClassName?: string;
|
|
10
19
|
}
|
|
11
20
|
declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
12
21
|
export { Input };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const inputVariants: (props?: ({
|
|
2
2
|
variant?: "soft" | "outline" | "white" | "ghost" | "slate" | null | undefined;
|
|
3
|
+
size?: "sm" | "lg" | "default" | null | undefined;
|
|
3
4
|
error?: boolean | null | undefined;
|
|
4
5
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
5
6
|
export declare const iconVariants: (props?: ({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
1
2
|
import { VariantProps } from 'class-variance-authority';
|
|
2
3
|
import { selectTriggerVariants } from './select-variants';
|
|
3
4
|
export type SelectOption = {
|
|
@@ -13,6 +14,15 @@ export interface SelectProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
|
|
|
13
14
|
disabled?: boolean;
|
|
14
15
|
className?: string;
|
|
15
16
|
label?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Classes for the trigger button itself.
|
|
19
|
+
*
|
|
20
|
+
* `className` lands on the outer `Field` wrapper, which is right for layout
|
|
21
|
+
* and useless for anything else: a consumer could not reach the control to
|
|
22
|
+
* change its height, ring or background. `size` covers the common case; this
|
|
23
|
+
* is the escape hatch for the rest. Mirrors `Input`'s `inputClassName`.
|
|
24
|
+
*/
|
|
25
|
+
triggerClassName?: string;
|
|
16
26
|
}
|
|
17
|
-
declare const Select: ({ options, value, onChange, placeholder, disabled, variant, error, className, label, }: SelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
declare const Select: ({ options, value, onChange, placeholder, disabled, variant, size, error, className, triggerClassName, label, ...props }: SelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
28
|
export { Select };
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export declare const selectTriggerVariants: (props?: ({
|
|
2
2
|
variant?: "soft" | "outline" | "white" | "ghost" | "slate" | null | undefined;
|
|
3
|
+
size?: "sm" | "lg" | "default" | null | undefined;
|
|
3
4
|
error?: boolean | null | undefined;
|
|
4
5
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
6
|
+
export declare const selectOptionVariants: (props?: ({
|
|
7
|
+
size?: "sm" | "lg" | "default" | null | undefined;
|
|
8
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
@@ -5,6 +5,15 @@ export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextArea
|
|
|
5
5
|
label?: string;
|
|
6
6
|
helperText?: string;
|
|
7
7
|
errorMessage?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Classes for the `<textarea>` itself.
|
|
10
|
+
*
|
|
11
|
+
* `className` lands on the outer `Field` wrapper, so `<Textarea
|
|
12
|
+
* className="rst:h-40" />` has always sized the wrapper and left the control
|
|
13
|
+
* alone. Mirrors `Input`'s `inputClassName` and `Select`'s
|
|
14
|
+
* `triggerClassName`: the hatch is named for the element it reaches.
|
|
15
|
+
*/
|
|
16
|
+
textareaClassName?: string;
|
|
8
17
|
}
|
|
9
18
|
declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
10
19
|
export { Textarea };
|
package/dist/meta.json
CHANGED