@scalewing/react 1.22.0 → 1.24.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/CHANGELOG.md +15 -0
- package/README.md +5 -3
- package/dist/components/BarChart.d.ts +11 -0
- package/dist/components/BarChart.js +9 -1
- package/dist/components/Field.d.ts +4 -1
- package/dist/css/breakpoints.d.ts +24 -3
- package/dist/css/breakpoints.js +27 -1
- package/dist/css/css-chart.d.ts +7 -0
- package/dist/css/css-chart.js +32 -1
- package/dist/css/css-chrome.js +15 -3
- package/dist/css/css-document.d.ts +14 -0
- package/dist/css/css-document.js +21 -7
- package/dist/css/css-field-changed.d.ts +13 -0
- package/dist/css/css-field-changed.js +64 -0
- package/dist/css/css-field.js +3 -13
- package/dist/css/css-responsive.js +4 -5
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/styles.css +72 -18
- package/dist/visibility-classes.d.ts +3 -3
- package/dist/visibility-classes.js +7 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @scalewing/react
|
|
2
2
|
|
|
3
|
+
## 1.24.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e6ed0d6: `Field` `changed` now also tints the control's fill (`docs/requests/teisoro-field-validation.md`, 2026-10-09 follow-up, Teisoro F-006-S11 task 1890), so a changed field no longer reads as the focused one: beside the accent border it keeps, the native control or the adorned frame takes the accent at 12 % as a background image over its own fill, which focus never draws. The value keeps 4.5:1 or more on the tint in every palette and scheme (6.74:1 at the lowest); the tint is on the text controls and select only (not a range, color or file input); while a placeholder shows the fill stays plain; on the tint an adorned field's prefix and suffix take the text color; a select keeps its chevron above it, placed for its size (`xs` included) by a private `--sw-select-chevron-inset` the select rule sets; and forced colors drop it and keep the `Highlight` border. No API change: same prop, same `sw-field-changed` class, no new tokens or dependencies. A consumer that layered its own background image on a changed control (none should: Scalewing owns the canvas) would now see it replaced.
|
|
8
|
+
|
|
9
|
+
`BarChartItem` takes an optional `tone` (`docs/requests/teisoro-bar-chart.md`, 2026-10-09 follow-up, Teisoro F-006-S11 task 1890): `'accent'`, `'success'`, `'warning'` or `'danger'` (new exported type `BarChartTone` and list `barChartTones`) colors a bar by what it means in place of its sign's default, such as a diverging chart's overage in warning and shortage in danger. New generated classes `sw-bar-chart-fill-accent`, `sw-bar-chart-fill-success`, `sw-bar-chart-fill-warning` and `sw-bar-chart-fill-danger`; a toned negative bar keeps `sw-bar-chart-fill-negative` and its diverging shape. Without a tone a chart is unchanged. An unknown tone throws a `RangeError`. Forced colors now draw every bar in `CanvasText`: a token fill was painted as the canvas there, so bars were invisible on their tracks. No new tokens and no new dependencies.
|
|
10
|
+
|
|
11
|
+
## 1.23.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- f51d196: A `Button` inside `Nav` is at least 44 × 44 px on a coarse pointer (`docs/requests/teisoro-nav.md`, Teisoro F-006-S11, UX-16), whatever its `size`, as `SectionNav` links, `CalendarButton` and the `ActionMenu` trigger already are; a fine pointer is unchanged. Only the minimum height and width grow, so a label beside its glyph keeps its line. No new classes, tokens or dependencies.
|
|
16
|
+
- f51d196: `hideBelow` and `hideFrom` take `lg` (64rem) (`docs/requests/teisoro-responsive-visibility.md`, 2026-10-09 follow-up, Teisoro F-006-S11, SET-3 and UX-16), for content that fits only from a laptop up, such as a row of labelled destinations a tablet shows as glyphs. New generated classes `sw-hide-below-lg` and `sw-hide-from-lg`; new exported types `VisibilityBreakpoint` (`'md' | 'lg'`) and `BreakpointDirection` (`'from' | 'below'`). The layout props keep the one layout breakpoint, `md`. `breakpointQuery(direction, breakpoint)` is now exported: the media query a hide class uses, so an app's `matchMedia` follows the same width; an unknown direction or breakpoint throws a `RangeError`. An unknown `hideBelow` or `hideFrom` now throws a `RangeError` (it produced a class with no rule). No new tokens and no new dependencies.
|
|
17
|
+
|
|
3
18
|
## 1.22.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -49,11 +49,11 @@ Import the CSS once at the application entry. Do not copy it into your source tr
|
|
|
49
49
|
|
|
50
50
|
`Box` `border` draws a hairline in the `border` color token: `border` (or `border={true}`) is solid, and `border="dashed"` is dashed (the generated `sw-border` and `sw-border-dashed` classes; a `style` you pass still wins), for a space to fill in by hand such as a blank on a printed form. It does not make the box look or act like an input. Every `Box`-based component takes it.
|
|
51
51
|
|
|
52
|
-
`Box hideBelow="md"` hides a region under 48rem and `hideFrom="md"` hides it at 48rem and wider, so a product can swap a wide layout for a narrow one without its own stylesheet. Hidden regions leave the accessibility tree; do not use this to protect data. `Box as="a"` is a layout link. Use `Button` for press actions. `Field` associates a native `<input>` or `<select>` with a label and token gap; native text controls inherit the generated document canvas. `Select` is a labeled listbox menu when the open list must match the canvas.
|
|
52
|
+
`Box hideBelow="md"` hides a region under 48rem and `hideFrom="md"` hides it at 48rem and wider, so a product can swap a wide layout for a narrow one without its own stylesheet. They also take `lg` (64rem), for content that fits only from a laptop up, such as a row of labelled destinations that a tablet shows as glyphs (name each glyph-only control, for example with `Tooltip relationship="label"`); the layout props keep `md`. `breakpointQuery('below', 'lg')` returns the query a hide class uses (`not all and (min-width: 64rem)`), for an app whose script must follow the same width. An unknown breakpoint throws a `RangeError`. Hidden regions leave the accessibility tree; do not use this to protect data. `Box as="a"` is a layout link. Use `Button` for press actions. `Field` associates a native `<input>` or `<select>` with a label and token gap; native text controls inherit the generated document canvas. `Select` is a labeled listbox menu when the open list must match the canvas.
|
|
53
53
|
|
|
54
54
|
`Field` supports optional `description`, `error`, and `required` on one native input, select, or textarea child. It preserves an existing `aria-describedby` and appends a stable ID for the hint, or for the error, which replaces the hint on screen while it is supplied. `error` sets `aria-invalid` and token-owned invalid styling. The error sits in a polite live region (`aria-live="polite"`) that is always rendered, empty while there is no error, so an error that appears while the person types, or after a submit, is announced once, politely, when its text is swapped in; it is never `role="alert"`, so a submit that finds several errors does not fire several assertive alerts at once. Empty, the region takes no room in the layout. The consumer owns validation and localized messages, and after a refused submit it moves focus to the first invalid control (or to one form-level summary). `Checkbox`, `RadioGroup` and `DateField` expose their `error` the same way. `invalid` marks the control invalid the same way (`aria-invalid` and the danger border, `Mark` in forced colors) without a message of its own, for fields whose one error is shown elsewhere, such as a count that adds up to nothing under a group of count fields: point each control's `aria-describedby` at that message so it is read with the field. The field's own polite region stays empty and its `description` stays on screen; `error` implies `invalid`. Passing `invalid`, even `invalid={false}` but not `invalid={undefined}`, needs one native control child and throws a `TypeError` on the first render otherwise, so a form learns it while the fields are valid, not on its first failed submit. `error` keeps its rule: it needs the native control only while it holds a message. `invalid` is `Field`'s only: `DateField`, `Select` and the other controls with `error` do not have it yet.
|
|
55
55
|
|
|
56
|
-
`Field` `changed` marks a value the person changed from a saved one, such as a field in a correction: the control's border, or an adorned field's frame, takes the accent color a hairline thicker (an inset shadow, so nothing moves; `Highlight` in forced colors). It is a cue beside the words, so say what the value was in `description`; it sets no ARIA state, and `invalid` and `error` win over it. Like `invalid`, passing it, even `false` but not `undefined`, needs one native control child from the first render (a `TypeError` otherwise).
|
|
56
|
+
`Field` `changed` marks a value the person changed from a saved one, such as a field in a correction: the control's border, or an adorned field's frame, takes the accent color a hairline thicker (an inset shadow, so nothing moves; `Highlight` in forced colors), and its fill takes a 12 % accent tint over its own background, so a changed field reads apart from the focused one, whose ring never tints. The value keeps 4.5:1 or more on the tint in every palette; the tint is on text controls and selects (not a range, color or file input), a select's chevron stays where its size puts it, while a placeholder shows the fill stays plain, on the tint an adorned field's prefix and suffix take the text color, and forced colors drop the tint. It is a cue beside the words, so say what the value was in `description`; it sets no ARIA state, and `invalid` and `error` win over it. Like `invalid`, passing it, even `false` but not `undefined`, needs one native control child from the first render (a `TypeError` otherwise).
|
|
57
57
|
|
|
58
58
|
```tsx
|
|
59
59
|
<Field invalid={countIsZero} label="$1 bills">
|
|
@@ -244,6 +244,8 @@ An option's optional `description` (a `ReactNode`) is secondary text for that op
|
|
|
244
244
|
|
|
245
245
|
`Tabs` is the strip for the sections of one page (`role="tablist"`, one `role="tab"` button per item; arrow keys, Home and End move and select), and `TabPanel` is each section's content, hidden while another tab is current. `sticky` (default `false`) keeps the strip at the top of the viewport, under `env(safe-area-inset-top)`, while a long panel scrolls under it, on the same layer as a sticky `AppHeader` (so a page uses one or the other at the top edge). The stuck strip is a full-bleed band of the page canvas, not a glass card: the canvas color at nine parts in ten over the glass blur, with the strip's hairline under it, solid canvas under Reduce Transparency and in forced colors. A sticky element only sticks within its parent: make the strip a direct child of the long page container that also holds the panels, not of a padded `Box` round the strip alone, and keep `overflow` visible on its ancestors. Put the page's side gutter on the title and the panels instead: each tab's inline padding is the md control's 16 px (spacing step 4), so the first label lines up with a `space-4` gutter when the strip runs edge to edge. A strip whose labels do not fit scrolls sideways, and while labels are scrolled out past an inline edge that edge draws the same inset shade as a wide `Table` (none in forced colors), so a phone shows there are more tabs.
|
|
246
246
|
|
|
247
|
+
`Nav` is a labelled `nav` of a workspace's destinations, links or buttons one token gap apart. A `Button` inside it is at least 44 × 44 px on a coarse pointer, whatever its `size`, so a compact row of destinations is still a touch target on a phone or a tablet.
|
|
248
|
+
|
|
247
249
|
`SectionNav` is the navigation between the sections of one area, such as a portal's pages: a labelled `nav` (`aria-label` or `aria-labelledby`) of links from `items` (`{ id, label, href, current?, icon? }`), quieter than a workspace's `Nav`. `current` is `'page'`, `'location'` (a page inside that section, such as a detail page under its list) or a boolean, written as `aria-current`; the current item is in the text color over an accent underline, or, from `verticalFrom` (a breakpoint) up, where the items stack as a side list, a bar at its start. `icon` is a decorative glyph before the label. `onNavigate` takes a plain press after preventing the browser's navigation, for a client router; a press with a modifier key or the middle button keeps the browser's behavior, and without `onNavigate` the links carry no handler, so a server component can render the nav. A coarse pointer gets 44 px targets. Empty or duplicate ids, empty labels and an unknown `verticalFrom` throw a `RangeError`. Use `Tabs` for panels on one page.
|
|
248
250
|
|
|
249
251
|
```tsx
|
|
@@ -283,7 +285,7 @@ An option's optional `description` (a `ReactNode`) is secondary text for that op
|
|
|
283
285
|
|
|
284
286
|
A header `TableCell` (`as="th"`) with `sort` (`'ascending'`, `'descending'` or `'none'`) and `onSort` is its column's sort control: its children become a button in the header's own style with a chevron (before the name in a `numeric` or end-aligned column), and the cell carries `aria-sort` only while it is the sorted column. `sort` on a data cell, or one of the two without the other, throws a `TypeError`. `Table` `layout="fixed"` (default `auto`) sizes the columns from the header row alone, so a filter that hides a row moves no column, and `verticalAlign="top"` (default `middle`) starts every cell of a tall row on its first line. `TableCell` `width` is `min` (an auto table's column takes the least room its content allows, on one line; set it on each cell of the column) or a size, `xs` to `xl` (4, 6, 8, 12 and 16 rem); in a fixed table a column without a width shares what is left, so leave one column, such as a description, unsized. The HTML `width` attribute is not accepted. Unknown values throw a `RangeError`.
|
|
285
287
|
|
|
286
|
-
`BarChart` `formatValue` writes the axis's values and each value without its own `valueLabel`, such as a currency. `diverging` puts zero in the middle of each track behind a hairline: a positive bar grows toward the inline end and a negative one toward the start, on one scale, and the axis reads minus the maximum, zero and the maximum. Without it every bar grows from the start.
|
|
288
|
+
`BarChart` `formatValue` writes the axis's values and each value without its own `valueLabel`, such as a currency. `diverging` puts zero in the middle of each track behind a hairline: a positive bar grows toward the inline end and a negative one toward the start, on one scale, and the axis reads minus the maximum, zero and the maximum. Without it every bar grows from the start. An item's `tone` (`BarChartTone`: `'accent'`, `'success'`, `'warning'` or `'danger'`, listed in the exported `barChartTones`) colors its bar by what it means in place of its sign's default (the accent, or danger for a negative value), such as a diverging chart's overage in `'warning'` and shortage in `'danger'`; a toned bar keeps its sign's place, every tone keeps 3:1 against the track, and an unknown tone throws a `RangeError`. Forced colors draw every bar in the system text color, so the value and its side of zero carry the meaning there.
|
|
287
289
|
|
|
288
290
|
`Spinner` shows indeterminate loading in small, medium, or large sizes. Supply localized `label` for the one announced status in a loading region. Use `decorative` on additional indicators beside that status so screen readers do not hear the same message repeatedly. Reduced motion leaves a static accented ring.
|
|
289
291
|
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { type HTMLAttributes } from 'react';
|
|
2
|
+
import { type BarChartTone, barChartTones } from '../css/css-chart.js';
|
|
3
|
+
export type { BarChartTone };
|
|
4
|
+
export { barChartTones };
|
|
2
5
|
export type BarChartItem = {
|
|
3
6
|
label: string;
|
|
4
7
|
value: number;
|
|
5
8
|
valueLabel?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Colors the bar by what it means, in place of its sign's default (the
|
|
11
|
+
* accent, or danger for a negative value): such as `'warning'` for an
|
|
12
|
+
* overage and `'danger'` for a shortage. The color is a cue beside the
|
|
13
|
+
* value and its label, never the only sign of the meaning. An unknown tone
|
|
14
|
+
* throws a `RangeError`.
|
|
15
|
+
*/
|
|
16
|
+
tone?: BarChartTone;
|
|
6
17
|
};
|
|
7
18
|
export type BarChartProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
8
19
|
'aria-label': string;
|
|
@@ -2,12 +2,20 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { barChartFillRatio, barChartScaleMax, formatBarChartValue, } from '@scalewing/tokens';
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
4
|
import { cx } from '../class-names.js';
|
|
5
|
+
import { barChartTones } from '../css/css-chart.js';
|
|
6
|
+
export { barChartTones };
|
|
7
|
+
/** The fill's classes: the sign's (which also shapes a diverging bar) and the tone's. */
|
|
8
|
+
function barChartFillClassName(item) {
|
|
9
|
+
if (item.tone !== undefined && !barChartTones.includes(item.tone))
|
|
10
|
+
throw new RangeError(`BarChart item tone must be one of ${barChartTones.join(', ')}; received ${String(item.tone)}`);
|
|
11
|
+
return cx('sw-bar-chart-fill', item.value < 0 && 'sw-bar-chart-fill-negative', item.tone && `sw-bar-chart-fill-${item.tone}`);
|
|
12
|
+
}
|
|
5
13
|
export const BarChart = forwardRef(function BarChart({ 'aria-label': ariaLabel, className, diverging = false, formatValue = formatBarChartValue, items, max, style, ...rest }, ref) {
|
|
6
14
|
const scaleMax = barChartScaleMax(items.map((item) => item.value), max);
|
|
7
15
|
const axis = diverging ? [-scaleMax, 0, scaleMax] : [0, scaleMax];
|
|
8
16
|
return (_jsxs("div", { ref: ref, className: cx('sw-bar-chart', diverging && 'sw-bar-chart-diverging', className), style: style, ...rest, children: [_jsx("ul", { "aria-label": ariaLabel, className: "sw-bar-chart-plot", children: items.map((item, index) => {
|
|
9
17
|
const ratio = barChartFillRatio(item.value, scaleMax);
|
|
10
|
-
return (_jsxs("li", { className: "sw-bar-chart-row", children: [_jsx("span", { className: "sw-bar-chart-label", children: item.label }), _jsx("span", { className: "sw-bar-chart-track", children: _jsx("span", { className:
|
|
18
|
+
return (_jsxs("li", { className: "sw-bar-chart-row", children: [_jsx("span", { className: "sw-bar-chart-label", children: item.label }), _jsx("span", { className: "sw-bar-chart-track", children: _jsx("span", { className: barChartFillClassName(item), style: {
|
|
11
19
|
'--sw-bar-fill': ratio,
|
|
12
20
|
} }) }), _jsx("span", { className: "sw-bar-chart-value", children: item.valueLabel ?? formatValue(item.value) })] }, `${item.label}-${index}`));
|
|
13
21
|
}) }), _jsxs("div", { "aria-hidden": "true", className: "sw-bar-chart-axis", children: [_jsx("span", {}), _jsx("span", { className: "sw-bar-chart-axis-track", children: axis.map((value) => (_jsx("span", { children: formatValue(value) }, value))) }), _jsx("span", {})] })] }));
|
|
@@ -21,7 +21,10 @@ export type FieldProps = {
|
|
|
21
21
|
/**
|
|
22
22
|
* Marks a value the person changed from a saved one, such as a field in a
|
|
23
23
|
* correction: the control's border (or an adorned frame's) in the accent
|
|
24
|
-
* color, thicker than the plain hairline
|
|
24
|
+
* color, thicker than the plain hairline, and its fill tinted with 12 % of
|
|
25
|
+
* the accent, so a changed field reads apart from the focused one (focus
|
|
26
|
+
* draws a ring and never tints). The value keeps 4.5:1 on the tint; while
|
|
27
|
+
* a placeholder shows, the fill stays plain. It is a cue, not the record:
|
|
25
28
|
* say what it was in `description` ("Was 25"). `invalid` and `error`
|
|
26
29
|
* win. Like `invalid`, passing it, even `false` but not `undefined`,
|
|
27
30
|
* requires one native control child from the first render.
|
|
@@ -1,8 +1,29 @@
|
|
|
1
|
+
/** The layout breakpoint: a phone below it, a wider screen from it. */
|
|
1
2
|
export declare const breakpointScale: {
|
|
2
3
|
readonly md: 48;
|
|
3
4
|
};
|
|
4
5
|
export type Breakpoint = keyof typeof breakpointScale;
|
|
5
6
|
export declare const breakpoints: Breakpoint[];
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The breakpoints `hideBelow` and `hideFrom` take: `md`, and `lg` (64rem)
|
|
9
|
+
* for content that fits only from a laptop up, such as a row of labelled
|
|
10
|
+
* destinations a tablet shows as glyphs. The layout props (`columnsBelow`,
|
|
11
|
+
* `stickyBelow`, `verticalFrom`, …) keep the one layout breakpoint.
|
|
12
|
+
*/
|
|
13
|
+
export declare const visibilityBreakpointScale: {
|
|
14
|
+
readonly lg: 64;
|
|
15
|
+
readonly md: 48;
|
|
16
|
+
};
|
|
17
|
+
export type VisibilityBreakpoint = keyof typeof visibilityBreakpointScale;
|
|
18
|
+
export declare const visibilityBreakpoints: VisibilityBreakpoint[];
|
|
19
|
+
/** Which side of a breakpoint: `from` it and wider, or `below` it. */
|
|
20
|
+
export type BreakpointDirection = 'from' | 'below';
|
|
21
|
+
export declare const breakpointDirections: readonly BreakpointDirection[];
|
|
22
|
+
export declare function hideClass(direction: BreakpointDirection, breakpoint: VisibilityBreakpoint): string;
|
|
23
|
+
/**
|
|
24
|
+
* The media query `hideBelow` (`'below'`) or `hideFrom` (`'from'`) uses at a
|
|
25
|
+
* breakpoint, such as `not all and (min-width: 64rem)` below `lg`, for an app
|
|
26
|
+
* that must follow the same width in script (a tooltip that names a glyph
|
|
27
|
+
* only while its label is hidden).
|
|
28
|
+
*/
|
|
29
|
+
export declare function breakpointQuery(direction: BreakpointDirection, breakpoint: VisibilityBreakpoint): string;
|
package/dist/css/breakpoints.js
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
|
+
/** The layout breakpoint: a phone below it, a wider screen from it. */
|
|
1
2
|
export const breakpointScale = {
|
|
2
3
|
md: 48,
|
|
3
4
|
};
|
|
4
5
|
export const breakpoints = Object.keys(breakpointScale);
|
|
6
|
+
/**
|
|
7
|
+
* The breakpoints `hideBelow` and `hideFrom` take: `md`, and `lg` (64rem)
|
|
8
|
+
* for content that fits only from a laptop up, such as a row of labelled
|
|
9
|
+
* destinations a tablet shows as glyphs. The layout props (`columnsBelow`,
|
|
10
|
+
* `stickyBelow`, `verticalFrom`, …) keep the one layout breakpoint.
|
|
11
|
+
*/
|
|
12
|
+
export const visibilityBreakpointScale = {
|
|
13
|
+
...breakpointScale,
|
|
14
|
+
lg: 64,
|
|
15
|
+
};
|
|
16
|
+
export const visibilityBreakpoints = Object.keys(visibilityBreakpointScale);
|
|
17
|
+
export const breakpointDirections = [
|
|
18
|
+
'from',
|
|
19
|
+
'below',
|
|
20
|
+
];
|
|
5
21
|
export function hideClass(direction, breakpoint) {
|
|
6
22
|
return `sw-hide-${direction}-${breakpoint}`;
|
|
7
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The media query `hideBelow` (`'below'`) or `hideFrom` (`'from'`) uses at a
|
|
26
|
+
* breakpoint, such as `not all and (min-width: 64rem)` below `lg`, for an app
|
|
27
|
+
* that must follow the same width in script (a tooltip that names a glyph
|
|
28
|
+
* only while its label is hidden).
|
|
29
|
+
*/
|
|
8
30
|
export function breakpointQuery(direction, breakpoint) {
|
|
9
|
-
|
|
31
|
+
if (!breakpointDirections.includes(direction))
|
|
32
|
+
throw new RangeError(`direction must be one of ${breakpointDirections.join(', ')}`);
|
|
33
|
+
if (!visibilityBreakpoints.includes(breakpoint))
|
|
34
|
+
throw new RangeError(`breakpoint must be one of ${visibilityBreakpoints.join(', ')}`);
|
|
35
|
+
const minWidth = `(min-width: ${visibilityBreakpointScale[breakpoint]}rem)`;
|
|
10
36
|
return direction === 'from' ? minWidth : `not all and ${minWidth}`;
|
|
11
37
|
}
|
package/dist/css/css-chart.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The meanings a bar's fill can take in place of its sign's default (the
|
|
3
|
+
* accent, or danger for a negative value), such as a diverging chart whose
|
|
4
|
+
* overage is a warning and whose shortage is danger.
|
|
5
|
+
*/
|
|
6
|
+
export declare const barChartTones: readonly ["accent", "success", "warning", "danger"];
|
|
7
|
+
export type BarChartTone = (typeof barChartTones)[number];
|
|
1
8
|
export declare function cssChartClasses(): string;
|
|
2
9
|
export declare function chartClassCatalog(): string[];
|
package/dist/css/css-chart.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import { typographyVariants } from '@scalewing/tokens';
|
|
2
2
|
const caption = typographyVariants.caption;
|
|
3
3
|
const data = typographyVariants.data;
|
|
4
|
+
/**
|
|
5
|
+
* The meanings a bar's fill can take in place of its sign's default (the
|
|
6
|
+
* accent, or danger for a negative value), such as a diverging chart whose
|
|
7
|
+
* overage is a warning and whose shortage is danger.
|
|
8
|
+
*/
|
|
9
|
+
export const barChartTones = [
|
|
10
|
+
'accent',
|
|
11
|
+
'success',
|
|
12
|
+
'warning',
|
|
13
|
+
'danger',
|
|
14
|
+
];
|
|
15
|
+
/*
|
|
16
|
+
* A tone's rule follows the negative fill's, at the same specificity, so a
|
|
17
|
+
* toned negative bar keeps its diverging geometry and takes the tone's
|
|
18
|
+
* color. Each tone keeps 3:1 against the track in every palette and scheme
|
|
19
|
+
* (`chart.test.tsx`). Forced colors would paint a token fill as the
|
|
20
|
+
* canvas, so every fill, toned or not, is drawn in the system text color
|
|
21
|
+
* after the tones: the value and its sign carry the meaning there.
|
|
22
|
+
*/
|
|
23
|
+
function toneRules() {
|
|
24
|
+
return barChartTones
|
|
25
|
+
.map((tone) => `.sw-bar-chart-fill-${tone} { background: var(--sw-color-${tone}); }`)
|
|
26
|
+
.join('\n');
|
|
27
|
+
}
|
|
4
28
|
/*
|
|
5
29
|
* A diverging chart puts zero in the middle of each track, marked by a
|
|
6
30
|
* hairline: a positive bar starts there and grows toward the inline end, a
|
|
@@ -98,6 +122,8 @@ export function cssChartClasses() {
|
|
|
98
122
|
background: var(--sw-color-danger);
|
|
99
123
|
}
|
|
100
124
|
|
|
125
|
+
${toneRules()}
|
|
126
|
+
|
|
101
127
|
.sw-bar-chart-value {
|
|
102
128
|
color: var(--sw-color-text);
|
|
103
129
|
font-family: var(--sw-font-sans);
|
|
@@ -123,7 +149,11 @@ export function cssChartClasses() {
|
|
|
123
149
|
justify-content: space-between;
|
|
124
150
|
}
|
|
125
151
|
|
|
126
|
-
${divergingRules()}
|
|
152
|
+
${divergingRules()}
|
|
153
|
+
|
|
154
|
+
@media (forced-colors: active) {
|
|
155
|
+
.sw-bar-chart-fill { background: CanvasText; }
|
|
156
|
+
}`;
|
|
127
157
|
}
|
|
128
158
|
export function chartClassCatalog() {
|
|
129
159
|
return [
|
|
@@ -135,6 +165,7 @@ export function chartClassCatalog() {
|
|
|
135
165
|
'sw-bar-chart-track',
|
|
136
166
|
'sw-bar-chart-fill',
|
|
137
167
|
'sw-bar-chart-fill-negative',
|
|
168
|
+
...barChartTones.map((tone) => `sw-bar-chart-fill-${tone}`),
|
|
138
169
|
'sw-bar-chart-value',
|
|
139
170
|
'sw-bar-chart-axis-track',
|
|
140
171
|
'sw-bar-chart-diverging',
|
package/dist/css/css-chrome.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { typographyVariants } from '@scalewing/tokens';
|
|
2
2
|
import { zIndex } from './stacking.js';
|
|
3
|
+
import { coarsePointerQuery, touchTarget } from './touch-target.js';
|
|
3
4
|
const caption = typographyVariants.caption;
|
|
4
5
|
const label = typographyVariants.label;
|
|
5
6
|
export function cssChromeClasses() {
|
|
@@ -36,6 +37,18 @@ export function cssChromeClasses() {
|
|
|
36
37
|
color: var(--sw-color-text);
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/*
|
|
41
|
+
* A button in a navigation is a destination: on a coarse pointer it grows to
|
|
42
|
+
* the 44 px touch target both ways, whatever its size, as a section link
|
|
43
|
+
* and a calendar button do. A label beside its glyph keeps its line.
|
|
44
|
+
*/
|
|
45
|
+
@media ${coarsePointerQuery} {
|
|
46
|
+
.sw-nav .sw-button {
|
|
47
|
+
min-height: ${touchTarget};
|
|
48
|
+
min-width: ${touchTarget};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
.sw-field-xs :is(select, input, textarea) {
|
|
40
53
|
font-size: ${caption.fontSize}px;
|
|
41
54
|
letter-spacing: ${caption.letterSpacing}px;
|
|
@@ -44,10 +57,9 @@ export function cssChromeClasses() {
|
|
|
44
57
|
padding-inline: var(--sw-control-xs-padding-inline);
|
|
45
58
|
}
|
|
46
59
|
|
|
60
|
+
/* The chevron's layers place themselves from this inset (selectChevronLayers). */
|
|
47
61
|
.sw-field-xs select {
|
|
48
|
-
|
|
49
|
-
calc(100% - var(--sw-space-3)) calc(50% - 1px),
|
|
50
|
-
calc(100% - calc(var(--sw-space-3) - var(--sw-space-1))) calc(50% - 1px);
|
|
62
|
+
--sw-select-chevron-inset: var(--sw-space-3);
|
|
51
63
|
height: calc(var(--sw-control-xs-min-height) - 1px - 1px);
|
|
52
64
|
min-height: 0;
|
|
53
65
|
padding-inline-end: calc(
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/** The native controls that take typed text: the canvas's text controls. */
|
|
2
|
+
export declare const typedInputs: string;
|
|
1
3
|
/** The frame every native text control draws: fill, hairline border, radius. */
|
|
2
4
|
export declare const controlSurface = "background-color: var(--sw-glass-fill);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-radius-sm);\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n padding-inline: var(--sw-control-md-padding-inline);";
|
|
3
5
|
export declare const disabledControlSurface = "background-color: var(--sw-color-subtle);\n border-style: dashed;\n cursor: not-allowed;\n opacity: 1;\n -webkit-text-fill-color: currentColor;";
|
|
4
6
|
export declare const disabledControlForcedColors = "border-color: GrayText;\n color: GrayText;\n -webkit-text-fill-color: GrayText;";
|
|
7
|
+
/**
|
|
8
|
+
* A select's chevron: two muted triangles drawn as background layers, so a
|
|
9
|
+
* rule that adds a layer (a changed field's tint) lists them with it. Their
|
|
10
|
+
* distance from the end is `--sw-select-chevron-inset`, which the select
|
|
11
|
+
* rule sets per size (space 4, and space 3 in an `xs` field), so every rule
|
|
12
|
+
* that lists the layers places them for the size it is in.
|
|
13
|
+
*/
|
|
14
|
+
export declare const selectChevronLayers: {
|
|
15
|
+
readonly image: "linear-gradient(45deg, transparent 50%, var(--sw-color-muted) 50%),\n linear-gradient(135deg, var(--sw-color-muted) 50%, transparent 50%)";
|
|
16
|
+
readonly position: "calc(100% - var(--sw-select-chevron-inset)) calc(50% - 1px),\n calc(100% - calc(var(--sw-select-chevron-inset) - var(--sw-space-1))) calc(50% - 1px)";
|
|
17
|
+
readonly size: "var(--sw-space-1) var(--sw-space-1),\n var(--sw-space-1) var(--sw-space-1)";
|
|
18
|
+
};
|
|
5
19
|
/** A script-only focus target in the canvas, at zero specificity. */
|
|
6
20
|
export declare const focusTargetSelector = ":where([data-theme] [tabindex='-1']:focus-visible)";
|
|
7
21
|
export declare function cssDocumentCanvas(): string;
|
package/dist/css/css-document.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { typographyVariants } from '@scalewing/tokens';
|
|
2
2
|
const body = typographyVariants.body;
|
|
3
|
-
|
|
3
|
+
/** The native controls that take typed text: the canvas's text controls. */
|
|
4
|
+
export const typedInputs = [
|
|
4
5
|
"input[type='text']",
|
|
5
6
|
"input[type='email']",
|
|
6
7
|
"input[type='number']",
|
|
@@ -34,6 +35,21 @@ export const disabledControlSurface = `background-color: var(--sw-color-subtle);
|
|
|
34
35
|
export const disabledControlForcedColors = `border-color: GrayText;
|
|
35
36
|
color: GrayText;
|
|
36
37
|
-webkit-text-fill-color: GrayText;`;
|
|
38
|
+
/**
|
|
39
|
+
* A select's chevron: two muted triangles drawn as background layers, so a
|
|
40
|
+
* rule that adds a layer (a changed field's tint) lists them with it. Their
|
|
41
|
+
* distance from the end is `--sw-select-chevron-inset`, which the select
|
|
42
|
+
* rule sets per size (space 4, and space 3 in an `xs` field), so every rule
|
|
43
|
+
* that lists the layers places them for the size it is in.
|
|
44
|
+
*/
|
|
45
|
+
export const selectChevronLayers = {
|
|
46
|
+
image: `linear-gradient(45deg, transparent 50%, var(--sw-color-muted) 50%),
|
|
47
|
+
linear-gradient(135deg, var(--sw-color-muted) 50%, transparent 50%)`,
|
|
48
|
+
position: `calc(100% - var(--sw-select-chevron-inset)) calc(50% - 1px),
|
|
49
|
+
calc(100% - calc(var(--sw-select-chevron-inset) - var(--sw-space-1))) calc(50% - 1px)`,
|
|
50
|
+
size: `var(--sw-space-1) var(--sw-space-1),
|
|
51
|
+
var(--sw-space-1) var(--sw-space-1)`,
|
|
52
|
+
};
|
|
37
53
|
/** A script-only focus target in the canvas, at zero specificity. */
|
|
38
54
|
export const focusTargetSelector = ":where([data-theme] [tabindex='-1']:focus-visible)";
|
|
39
55
|
export function cssDocumentCanvas() {
|
|
@@ -103,16 +119,14 @@ ${focusTargetSelector} {
|
|
|
103
119
|
|
|
104
120
|
[data-theme] select {
|
|
105
121
|
${controlSurface}
|
|
122
|
+
--sw-select-chevron-inset: var(--sw-space-4);
|
|
106
123
|
appearance: none;
|
|
107
124
|
background-image:
|
|
108
|
-
|
|
109
|
-
linear-gradient(135deg, var(--sw-color-muted) 50%, transparent 50%);
|
|
125
|
+
${selectChevronLayers.image};
|
|
110
126
|
background-position:
|
|
111
|
-
|
|
112
|
-
calc(100% - calc(var(--sw-space-4) - var(--sw-space-1))) calc(50% - 1px);
|
|
127
|
+
${selectChevronLayers.position};
|
|
113
128
|
background-repeat: no-repeat;
|
|
114
|
-
background-size:
|
|
115
|
-
var(--sw-space-1) var(--sw-space-1);
|
|
129
|
+
background-size: ${selectChevronLayers.size};
|
|
116
130
|
box-sizing: content-box;
|
|
117
131
|
height: calc(var(--sw-control-md-min-height) - 1px - 1px);
|
|
118
132
|
min-height: 0;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much accent a changed control's fill takes: the share of the accent
|
|
3
|
+
* layered over the control's own background (the glass fill, or the subtle
|
|
4
|
+
* fill of a disabled control). The value keeps 4.5:1 or more on it in every
|
|
5
|
+
* palette and scheme (`field-changed.test.tsx`).
|
|
6
|
+
*/
|
|
7
|
+
export declare const changedTintStrength = 0.12;
|
|
8
|
+
/**
|
|
9
|
+
* The tint as one background image, so it lies over the control's background
|
|
10
|
+
* color instead of replacing it.
|
|
11
|
+
*/
|
|
12
|
+
export declare const changedTintLayer = "linear-gradient(color-mix(in srgb, var(--sw-color-accent) 12%, transparent), color-mix(in srgb, var(--sw-color-accent) 12%, transparent))";
|
|
13
|
+
export declare function cssFieldChangedClasses(): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { selectChevronLayers, typedInputs } from './css-document.js';
|
|
2
|
+
/**
|
|
3
|
+
* How much accent a changed control's fill takes: the share of the accent
|
|
4
|
+
* layered over the control's own background (the glass fill, or the subtle
|
|
5
|
+
* fill of a disabled control). The value keeps 4.5:1 or more on it in every
|
|
6
|
+
* palette and scheme (`field-changed.test.tsx`).
|
|
7
|
+
*/
|
|
8
|
+
export const changedTintStrength = 0.12;
|
|
9
|
+
const tintColor = `color-mix(in srgb, var(--sw-color-accent) ${changedTintStrength * 100}%, transparent)`;
|
|
10
|
+
/**
|
|
11
|
+
* The tint as one background image, so it lies over the control's background
|
|
12
|
+
* color instead of replacing it.
|
|
13
|
+
*/
|
|
14
|
+
export const changedTintLayer = `linear-gradient(${tintColor}, ${tintColor})`;
|
|
15
|
+
/** An adorned frame that holds a value (no placeholder showing). */
|
|
16
|
+
const tintedFrame = '.sw-field-changed .sw-field-adorned:not(:has(> input:placeholder-shown))';
|
|
17
|
+
/*
|
|
18
|
+
* A changed value: the accent border, a hairline thicker through an inset
|
|
19
|
+
* shadow so nothing moves, and the accent tint over the fill, on the control
|
|
20
|
+
* or on an adorned frame (whose input is transparent and draws no border, so
|
|
21
|
+
* the tint shows once). Focus draws a ring and never tints, so "changed" and
|
|
22
|
+
* "where I am" read apart. While a placeholder shows there is no value to
|
|
23
|
+
* mark and the fill stays plain, so the muted placeholder keeps its contrast;
|
|
24
|
+
* on the tint, an adorned frame's prefix and suffix take the text color. The
|
|
25
|
+
* tint is for the text controls and select (not a range, color or file
|
|
26
|
+
* input). A select lists its chevron above the tint, placed for its size by
|
|
27
|
+
* `--sw-select-chevron-inset`. Forced colors drop the tint and the shadow
|
|
28
|
+
* and keep the Highlight border.
|
|
29
|
+
*/
|
|
30
|
+
export function cssFieldChangedClasses() {
|
|
31
|
+
return `[data-theme] .sw-field-changed > :is(input, select, textarea),
|
|
32
|
+
.sw-field-changed .sw-field-adorned {
|
|
33
|
+
border-color: var(--sw-color-accent);
|
|
34
|
+
box-shadow: inset 0 0 0 1px var(--sw-color-accent);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-theme] .sw-field-changed > :is(${typedInputs}):not(:placeholder-shown),
|
|
38
|
+
${tintedFrame} {
|
|
39
|
+
background-image: ${changedTintLayer};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[data-theme] .sw-field-changed > select {
|
|
43
|
+
background-image:
|
|
44
|
+
${selectChevronLayers.image},
|
|
45
|
+
${changedTintLayer};
|
|
46
|
+
background-position:
|
|
47
|
+
${selectChevronLayers.position},
|
|
48
|
+
0 0;
|
|
49
|
+
background-size: ${selectChevronLayers.size},
|
|
50
|
+
100% 100%;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
${tintedFrame} > :is(.sw-field-prefix, .sw-field-suffix) {
|
|
54
|
+
color: var(--sw-color-text);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (forced-colors: active) {
|
|
58
|
+
[data-theme] .sw-field-changed > :is(input, select, textarea),
|
|
59
|
+
.sw-field-changed .sw-field-adorned { border-color: Highlight; box-shadow: inset 0 0 0 1px Highlight; }
|
|
60
|
+
[data-theme] .sw-field-changed > :is(${typedInputs}):not(:placeholder-shown),
|
|
61
|
+
${tintedFrame} { background-image: none; }
|
|
62
|
+
[data-theme] .sw-field-changed > select { background-image: ${selectChevronLayers.image}; }
|
|
63
|
+
}`;
|
|
64
|
+
}
|
package/dist/css/css-field.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { typographyVariants } from '@scalewing/tokens';
|
|
2
2
|
import { controlSurface, disabledControlForcedColors, disabledControlSurface, } from './css-document.js';
|
|
3
|
+
import { cssFieldChangedClasses } from './css-field-changed.js';
|
|
3
4
|
const caption = typographyVariants.caption;
|
|
4
5
|
/*
|
|
5
6
|
* A prefix or suffix sits inside the control frame: the wrapper draws the
|
|
@@ -80,24 +81,13 @@ export function cssFieldClasses() {
|
|
|
80
81
|
border-color: var(--sw-color-danger);
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
/*
|
|
84
|
-
* A changed value: the accent border, a hairline thicker through an inset
|
|
85
|
-
* shadow so nothing moves, on the control or on an adorned frame (whose
|
|
86
|
-
* input draws no border of its own).
|
|
87
|
-
*/
|
|
88
|
-
[data-theme] .sw-field-changed > :is(input, select, textarea),
|
|
89
|
-
.sw-field-changed .sw-field-adorned {
|
|
90
|
-
border-color: var(--sw-color-accent);
|
|
91
|
-
box-shadow: inset 0 0 0 1px var(--sw-color-accent);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
84
|
${adornmentRules()}
|
|
95
85
|
|
|
86
|
+
${cssFieldChangedClasses()}
|
|
87
|
+
|
|
96
88
|
@media (forced-colors: active) {
|
|
97
89
|
[data-theme] .sw-field-invalid :is(input, select, textarea),
|
|
98
90
|
.sw-field-invalid .sw-field-adorned { border-color: Mark; }
|
|
99
|
-
[data-theme] .sw-field-changed > :is(input, select, textarea),
|
|
100
|
-
.sw-field-changed .sw-field-adorned { border-color: Highlight; box-shadow: inset 0 0 0 1px Highlight; }
|
|
101
91
|
.sw-field-prefix, .sw-field-suffix { color: CanvasText; }
|
|
102
92
|
.sw-field-adorned:has(> input:disabled) {
|
|
103
93
|
${disabledControlForcedColors}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const directions = ['from', 'below'];
|
|
1
|
+
import { breakpointDirections, breakpointQuery, hideClass, visibilityBreakpoints, } from './breakpoints.js';
|
|
3
2
|
export function cssResponsiveClasses() {
|
|
4
|
-
return
|
|
5
|
-
.flatMap((breakpoint) =>
|
|
3
|
+
return visibilityBreakpoints
|
|
4
|
+
.flatMap((breakpoint) => breakpointDirections.map((direction) => `@media ${breakpointQuery(direction, breakpoint)} {
|
|
6
5
|
.${hideClass(direction, breakpoint)} { display: none; }
|
|
7
6
|
}`))
|
|
8
7
|
.join('\n\n');
|
|
9
8
|
}
|
|
10
9
|
export function responsiveClassCatalog() {
|
|
11
|
-
return
|
|
10
|
+
return visibilityBreakpoints.flatMap((breakpoint) => breakpointDirections.map((direction) => hideClass(direction, breakpoint)));
|
|
12
11
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { Tooltip, type TooltipProps, type TooltipRelationship, } from './compone
|
|
|
12
12
|
export { RadioGroup, type RadioGroupOption, type RadioGroupProps, } from './components/RadioGroup.js';
|
|
13
13
|
export { AppHeader, type AppHeaderProps } from './components/AppHeader.js';
|
|
14
14
|
export { Badge, type BadgeProps, type BadgeSize, type BadgeTone, badgeSizes, badgeTones, } from './components/Badge.js';
|
|
15
|
-
export { BarChart, type BarChartItem, type BarChartProps, } from './components/BarChart.js';
|
|
15
|
+
export { BarChart, type BarChartItem, type BarChartProps, type BarChartTone, barChartTones, } from './components/BarChart.js';
|
|
16
16
|
export { Button, type ButtonProps, type ButtonSize, type ButtonVariant, } from './components/Button.js';
|
|
17
17
|
export { Box, type BoxBorder, type BoxElement, type BoxProps, } from './components/Box.js';
|
|
18
18
|
export { ButtonGroup, type ButtonGroupJustify, type ButtonGroupProps, } from './components/ButtonGroup.js';
|
|
@@ -37,7 +37,8 @@ export { Text, type TextAlign, type TextElement, type TextProps, } from './compo
|
|
|
37
37
|
export { ThemeProvider, useTheme, type ThemeProviderProps, } from './theme/ThemeProvider.js';
|
|
38
38
|
export { utilityClassCatalog } from './css/stylesheet.js';
|
|
39
39
|
export type { VisibilityProps } from './visibility-classes.js';
|
|
40
|
-
export type { Breakpoint } from './css/breakpoints.js';
|
|
40
|
+
export type { Breakpoint, BreakpointDirection, VisibilityBreakpoint, } from './css/breakpoints.js';
|
|
41
|
+
export { breakpointQuery } from './css/breakpoints.js';
|
|
41
42
|
export { Separator } from './components/Separator.js';
|
|
42
43
|
export type { SeparatorOrientation, SeparatorProps, } from './components/Separator.js';
|
|
43
44
|
export { Grid } from './components/Grid.js';
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export { Tooltip, } from './components/Tooltip.js';
|
|
|
11
11
|
export { RadioGroup, } from './components/RadioGroup.js';
|
|
12
12
|
export { AppHeader } from './components/AppHeader.js';
|
|
13
13
|
export { Badge, badgeSizes, badgeTones, } from './components/Badge.js';
|
|
14
|
-
export { BarChart, } from './components/BarChart.js';
|
|
14
|
+
export { BarChart, barChartTones, } from './components/BarChart.js';
|
|
15
15
|
export { Button, } from './components/Button.js';
|
|
16
16
|
export { Box, } from './components/Box.js';
|
|
17
17
|
export { ButtonGroup, } from './components/ButtonGroup.js';
|
|
@@ -32,6 +32,7 @@ export { Table, TableBody, TableCell, TableHeader, TableRow, } from './component
|
|
|
32
32
|
export { Text, } from './components/Text.js';
|
|
33
33
|
export { ThemeProvider, useTheme, } from './theme/ThemeProvider.js';
|
|
34
34
|
export { utilityClassCatalog } from './css/stylesheet.js';
|
|
35
|
+
export { breakpointQuery } from './css/breakpoints.js';
|
|
35
36
|
export { Separator } from './components/Separator.js';
|
|
36
37
|
export { Grid } from './components/Grid.js';
|
|
37
38
|
export { DenominationGrid } from './components/DenominationGrid.js';
|
package/dist/styles.css
CHANGED
|
@@ -644,13 +644,14 @@ body {
|
|
|
644
644
|
font-family: inherit;
|
|
645
645
|
font-size: inherit;
|
|
646
646
|
padding-inline: var(--sw-control-md-padding-inline);
|
|
647
|
+
--sw-select-chevron-inset: var(--sw-space-4);
|
|
647
648
|
appearance: none;
|
|
648
649
|
background-image:
|
|
649
650
|
linear-gradient(45deg, transparent 50%, var(--sw-color-muted) 50%),
|
|
650
651
|
linear-gradient(135deg, var(--sw-color-muted) 50%, transparent 50%);
|
|
651
652
|
background-position:
|
|
652
|
-
calc(100% - var(--sw-
|
|
653
|
-
calc(100% - calc(var(--sw-
|
|
653
|
+
calc(100% - var(--sw-select-chevron-inset)) calc(50% - 1px),
|
|
654
|
+
calc(100% - calc(var(--sw-select-chevron-inset) - var(--sw-space-1))) calc(50% - 1px);
|
|
654
655
|
background-repeat: no-repeat;
|
|
655
656
|
background-size: var(--sw-space-1) var(--sw-space-1),
|
|
656
657
|
var(--sw-space-1) var(--sw-space-1);
|
|
@@ -1951,6 +1952,11 @@ body {
|
|
|
1951
1952
|
background: var(--sw-color-danger);
|
|
1952
1953
|
}
|
|
1953
1954
|
|
|
1955
|
+
.sw-bar-chart-fill-accent { background: var(--sw-color-accent); }
|
|
1956
|
+
.sw-bar-chart-fill-success { background: var(--sw-color-success); }
|
|
1957
|
+
.sw-bar-chart-fill-warning { background: var(--sw-color-warning); }
|
|
1958
|
+
.sw-bar-chart-fill-danger { background: var(--sw-color-danger); }
|
|
1959
|
+
|
|
1954
1960
|
.sw-bar-chart-value {
|
|
1955
1961
|
color: var(--sw-color-text);
|
|
1956
1962
|
font-family: var(--sw-font-sans);
|
|
@@ -2008,6 +2014,10 @@ body {
|
|
|
2008
2014
|
.sw-bar-chart-diverging .sw-bar-chart-track::before { background: CanvasText; }
|
|
2009
2015
|
}
|
|
2010
2016
|
|
|
2017
|
+
@media (forced-colors: active) {
|
|
2018
|
+
.sw-bar-chart-fill { background: CanvasText; }
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2011
2021
|
.sw-app-header {
|
|
2012
2022
|
background: var(--sw-glass-fill);
|
|
2013
2023
|
border: 1px solid var(--sw-glass-border);
|
|
@@ -2041,6 +2051,18 @@ body {
|
|
|
2041
2051
|
color: var(--sw-color-text);
|
|
2042
2052
|
}
|
|
2043
2053
|
|
|
2054
|
+
/*
|
|
2055
|
+
* A button in a navigation is a destination: on a coarse pointer it grows to
|
|
2056
|
+
* the 44 px touch target both ways, whatever its size, as a section link
|
|
2057
|
+
* and a calendar button do. A label beside its glyph keeps its line.
|
|
2058
|
+
*/
|
|
2059
|
+
@media (pointer: coarse) {
|
|
2060
|
+
.sw-nav .sw-button {
|
|
2061
|
+
min-height: var(--sw-control-md-min-height);
|
|
2062
|
+
min-width: var(--sw-control-md-min-height);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2044
2066
|
.sw-field-xs :is(select, input, textarea) {
|
|
2045
2067
|
font-size: 13px;
|
|
2046
2068
|
letter-spacing: -0.08px;
|
|
@@ -2049,10 +2071,9 @@ body {
|
|
|
2049
2071
|
padding-inline: var(--sw-control-xs-padding-inline);
|
|
2050
2072
|
}
|
|
2051
2073
|
|
|
2074
|
+
/* The chevron's layers place themselves from this inset (selectChevronLayers). */
|
|
2052
2075
|
.sw-field-xs select {
|
|
2053
|
-
|
|
2054
|
-
calc(100% - var(--sw-space-3)) calc(50% - 1px),
|
|
2055
|
-
calc(100% - calc(var(--sw-space-3) - var(--sw-space-1))) calc(50% - 1px);
|
|
2076
|
+
--sw-select-chevron-inset: var(--sw-space-3);
|
|
2056
2077
|
height: calc(var(--sw-control-xs-min-height) - 1px - 1px);
|
|
2057
2078
|
min-height: 0;
|
|
2058
2079
|
padding-inline-end: calc(
|
|
@@ -3599,17 +3620,6 @@ body {
|
|
|
3599
3620
|
border-color: var(--sw-color-danger);
|
|
3600
3621
|
}
|
|
3601
3622
|
|
|
3602
|
-
/*
|
|
3603
|
-
* A changed value: the accent border, a hairline thicker through an inset
|
|
3604
|
-
* shadow so nothing moves, on the control or on an adorned frame (whose
|
|
3605
|
-
* input draws no border of its own).
|
|
3606
|
-
*/
|
|
3607
|
-
[data-theme] .sw-field-changed > :is(input, select, textarea),
|
|
3608
|
-
.sw-field-changed .sw-field-adorned {
|
|
3609
|
-
border-color: var(--sw-color-accent);
|
|
3610
|
-
box-shadow: inset 0 0 0 1px var(--sw-color-accent);
|
|
3611
|
-
}
|
|
3612
|
-
|
|
3613
3623
|
.sw-field-adorned {
|
|
3614
3624
|
background-color: var(--sw-glass-fill);
|
|
3615
3625
|
border: 1px solid var(--sw-color-border);
|
|
@@ -3673,11 +3683,47 @@ body {
|
|
|
3673
3683
|
|
|
3674
3684
|
[data-theme] .sw-field-adorned > input:disabled { background: transparent; }
|
|
3675
3685
|
|
|
3686
|
+
[data-theme] .sw-field-changed > :is(input, select, textarea),
|
|
3687
|
+
.sw-field-changed .sw-field-adorned {
|
|
3688
|
+
border-color: var(--sw-color-accent);
|
|
3689
|
+
box-shadow: inset 0 0 0 1px var(--sw-color-accent);
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
[data-theme] .sw-field-changed > :is(input[type='text'], input[type='email'], input[type='number'], input[type='search'], input[type='url'], input[type='password'], input:not([type]), textarea):not(:placeholder-shown),
|
|
3693
|
+
.sw-field-changed .sw-field-adorned:not(:has(> input:placeholder-shown)) {
|
|
3694
|
+
background-image: linear-gradient(color-mix(in srgb, var(--sw-color-accent) 12%, transparent), color-mix(in srgb, var(--sw-color-accent) 12%, transparent));
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
[data-theme] .sw-field-changed > select {
|
|
3698
|
+
background-image:
|
|
3699
|
+
linear-gradient(45deg, transparent 50%, var(--sw-color-muted) 50%),
|
|
3700
|
+
linear-gradient(135deg, var(--sw-color-muted) 50%, transparent 50%),
|
|
3701
|
+
linear-gradient(color-mix(in srgb, var(--sw-color-accent) 12%, transparent), color-mix(in srgb, var(--sw-color-accent) 12%, transparent));
|
|
3702
|
+
background-position:
|
|
3703
|
+
calc(100% - var(--sw-select-chevron-inset)) calc(50% - 1px),
|
|
3704
|
+
calc(100% - calc(var(--sw-select-chevron-inset) - var(--sw-space-1))) calc(50% - 1px),
|
|
3705
|
+
0 0;
|
|
3706
|
+
background-size: var(--sw-space-1) var(--sw-space-1),
|
|
3707
|
+
var(--sw-space-1) var(--sw-space-1),
|
|
3708
|
+
100% 100%;
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
.sw-field-changed .sw-field-adorned:not(:has(> input:placeholder-shown)) > :is(.sw-field-prefix, .sw-field-suffix) {
|
|
3712
|
+
color: var(--sw-color-text);
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3676
3715
|
@media (forced-colors: active) {
|
|
3677
|
-
[data-theme] .sw-field-invalid :is(input, select, textarea),
|
|
3678
|
-
.sw-field-invalid .sw-field-adorned { border-color: Mark; }
|
|
3679
3716
|
[data-theme] .sw-field-changed > :is(input, select, textarea),
|
|
3680
3717
|
.sw-field-changed .sw-field-adorned { border-color: Highlight; box-shadow: inset 0 0 0 1px Highlight; }
|
|
3718
|
+
[data-theme] .sw-field-changed > :is(input[type='text'], input[type='email'], input[type='number'], input[type='search'], input[type='url'], input[type='password'], input:not([type]), textarea):not(:placeholder-shown),
|
|
3719
|
+
.sw-field-changed .sw-field-adorned:not(:has(> input:placeholder-shown)) { background-image: none; }
|
|
3720
|
+
[data-theme] .sw-field-changed > select { background-image: linear-gradient(45deg, transparent 50%, var(--sw-color-muted) 50%),
|
|
3721
|
+
linear-gradient(135deg, var(--sw-color-muted) 50%, transparent 50%); }
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3724
|
+
@media (forced-colors: active) {
|
|
3725
|
+
[data-theme] .sw-field-invalid :is(input, select, textarea),
|
|
3726
|
+
.sw-field-invalid .sw-field-adorned { border-color: Mark; }
|
|
3681
3727
|
.sw-field-prefix, .sw-field-suffix { color: CanvasText; }
|
|
3682
3728
|
.sw-field-adorned:has(> input:disabled) {
|
|
3683
3729
|
border-color: GrayText;
|
|
@@ -3873,6 +3919,14 @@ body {
|
|
|
3873
3919
|
.sw-hide-below-md { display: none; }
|
|
3874
3920
|
}
|
|
3875
3921
|
|
|
3922
|
+
@media (min-width: 64rem) {
|
|
3923
|
+
.sw-hide-from-lg { display: none; }
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
@media not all and (min-width: 64rem) {
|
|
3927
|
+
.sw-hide-below-lg { display: none; }
|
|
3928
|
+
}
|
|
3929
|
+
|
|
3876
3930
|
.sw-border { border: 1px solid var(--sw-color-border); }
|
|
3877
3931
|
.sw-border-dashed { border: 1px dashed var(--sw-color-border); }
|
|
3878
3932
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type VisibilityBreakpoint } from './css/breakpoints.js';
|
|
2
2
|
export type VisibilityProps = {
|
|
3
|
-
hideBelow?:
|
|
4
|
-
hideFrom?:
|
|
3
|
+
hideBelow?: VisibilityBreakpoint;
|
|
4
|
+
hideFrom?: VisibilityBreakpoint;
|
|
5
5
|
};
|
|
6
6
|
export declare function visibilityClassNames({ hideBelow, hideFrom, }: VisibilityProps): string[];
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { hideClass } from './css/breakpoints.js';
|
|
1
|
+
import { hideClass, visibilityBreakpoints, } from './css/breakpoints.js';
|
|
2
|
+
function assertBreakpoint(prop, breakpoint) {
|
|
3
|
+
if (breakpoint && !visibilityBreakpoints.includes(breakpoint))
|
|
4
|
+
throw new RangeError(`${prop} must be one of ${visibilityBreakpoints.join(', ')}`);
|
|
5
|
+
}
|
|
2
6
|
export function visibilityClassNames({ hideBelow, hideFrom, }) {
|
|
7
|
+
assertBreakpoint('hideBelow', hideBelow);
|
|
8
|
+
assertBreakpoint('hideFrom', hideFrom);
|
|
3
9
|
return [
|
|
4
10
|
...(hideBelow ? [hideClass('below', hideBelow)] : []),
|
|
5
11
|
...(hideFrom ? [hideClass('from', hideFrom)] : []),
|