@wallarm-org/design-system 1.6.2 → 1.7.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/dist/components/EmptyState/EmptyState.js +14 -10
- package/dist/components/EmptyState/EmptyStateContext.d.ts +3 -0
- package/dist/components/EmptyState/EmptyStateContext.js +5 -0
- package/dist/components/EmptyState/EmptyStateIllustration.js +2 -1
- package/dist/components/EmptyState/EmptyStateMessage.js +6 -1
- package/dist/components/EmptyState/EmptyStateTitle.js +6 -1
- package/dist/components/EmptyState/classes.d.ts +27 -0
- package/dist/components/EmptyState/classes.js +27 -4
- package/dist/icons/Shapes.d.ts +3 -0
- package/dist/icons/Shapes.js +12 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/metadata/components.json +40 -9
- package/dist/theme/Pixel.stories.tsx +1 -1
- package/dist/theme/index.css +1 -0
- package/dist/theme/semantic.css +28 -0
- package/dist/theme/utilities/empty-state-medallion.css +18 -0
- package/package.json +1 -1
|
@@ -2,18 +2,22 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { cn } from "../../utils/cn.js";
|
|
3
3
|
import { TestIdProvider } from "../../utils/testId.js";
|
|
4
4
|
import { emptyStateVariants } from "./classes.js";
|
|
5
|
+
import { EmptyStateProvider } from "./EmptyStateContext.js";
|
|
5
6
|
const EmptyState = ({ ref, type = 'collection-empty', className, children, 'data-testid': testId, ...props })=>/*#__PURE__*/ jsx(TestIdProvider, {
|
|
6
7
|
value: testId,
|
|
7
|
-
children: /*#__PURE__*/ jsx(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
children: /*#__PURE__*/ jsx(EmptyStateProvider, {
|
|
9
|
+
value: type,
|
|
10
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
11
|
+
...props,
|
|
12
|
+
ref: ref,
|
|
13
|
+
role: "status",
|
|
14
|
+
"data-slot": "empty-state",
|
|
15
|
+
"data-testid": testId,
|
|
16
|
+
className: cn(emptyStateVariants({
|
|
17
|
+
type
|
|
18
|
+
}), className),
|
|
19
|
+
children: children
|
|
20
|
+
})
|
|
17
21
|
})
|
|
18
22
|
});
|
|
19
23
|
EmptyState.displayName = 'EmptyState';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
const EmptyStateContext = /*#__PURE__*/ createContext('collection-empty');
|
|
3
|
+
const EmptyStateProvider = EmptyStateContext.Provider;
|
|
4
|
+
const useEmptyStateType = ()=>useContext(EmptyStateContext);
|
|
5
|
+
export { EmptyStateProvider, useEmptyStateType };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../utils/cn.js";
|
|
3
3
|
import { useTestId } from "../../utils/testId.js";
|
|
4
|
+
import { emptyStateIllustrationVariants } from "./classes.js";
|
|
4
5
|
const EmptyStateIllustration = ({ ref, className, children, ...props })=>{
|
|
5
6
|
const testId = useTestId('illustration');
|
|
6
7
|
return /*#__PURE__*/ jsx("div", {
|
|
@@ -8,7 +9,7 @@ const EmptyStateIllustration = ({ ref, className, children, ...props })=>{
|
|
|
8
9
|
ref: ref,
|
|
9
10
|
"data-slot": "empty-state-illustration",
|
|
10
11
|
"data-testid": testId,
|
|
11
|
-
className: cn(
|
|
12
|
+
className: cn(emptyStateIllustrationVariants(), className),
|
|
12
13
|
children: children
|
|
13
14
|
});
|
|
14
15
|
};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../utils/cn.js";
|
|
3
3
|
import { useTestId } from "../../utils/testId.js";
|
|
4
|
+
import { emptyStateMessageVariants } from "./classes.js";
|
|
5
|
+
import { useEmptyStateType } from "./EmptyStateContext.js";
|
|
4
6
|
const EmptyStateMessage = ({ ref, className, children, ...props })=>{
|
|
5
7
|
const testId = useTestId('message');
|
|
8
|
+
const type = useEmptyStateType();
|
|
6
9
|
return /*#__PURE__*/ jsx("div", {
|
|
7
10
|
...props,
|
|
8
11
|
ref: ref,
|
|
9
12
|
"data-slot": "empty-state-message",
|
|
10
13
|
"data-testid": testId,
|
|
11
|
-
className: cn(
|
|
14
|
+
className: cn(emptyStateMessageVariants({
|
|
15
|
+
type
|
|
16
|
+
}), className),
|
|
12
17
|
children: children
|
|
13
18
|
});
|
|
14
19
|
};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../utils/cn.js";
|
|
3
3
|
import { useTestId } from "../../utils/testId.js";
|
|
4
|
+
import { emptyStateTitleVariants } from "./classes.js";
|
|
5
|
+
import { useEmptyStateType } from "./EmptyStateContext.js";
|
|
4
6
|
const EmptyStateTitle = ({ ref, className, children, ...props })=>{
|
|
5
7
|
const testId = useTestId('title');
|
|
8
|
+
const type = useEmptyStateType();
|
|
6
9
|
return /*#__PURE__*/ jsx("p", {
|
|
7
10
|
...props,
|
|
8
11
|
ref: ref,
|
|
9
12
|
"data-testid": testId,
|
|
10
13
|
"data-slot": "empty-state-title",
|
|
11
|
-
className: cn(
|
|
14
|
+
className: cn(emptyStateTitleVariants({
|
|
15
|
+
type
|
|
16
|
+
}), className),
|
|
12
17
|
children: children
|
|
13
18
|
});
|
|
14
19
|
};
|
|
@@ -2,3 +2,30 @@ export type EmptyStateType = 'collection-empty' | 'no-results';
|
|
|
2
2
|
export declare const emptyStateVariants: (props?: ({
|
|
3
3
|
type?: "collection-empty" | "no-results" | null | undefined;
|
|
4
4
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
5
|
+
/**
|
|
6
|
+
* The medallion: a 36px raised tile holding a 20px glyph, which with `p-8` fills
|
|
7
|
+
* the content box exactly — the hairline is an inset ring in the shadow list,
|
|
8
|
+
* not a real border, so it doesn't eat into the padding. `text-xl` sets the
|
|
9
|
+
* 20px em box, so an icon left at its default `size='inherit'` lands on spec
|
|
10
|
+
* without every call site restating a size.
|
|
11
|
+
*
|
|
12
|
+
* The Figma master's placeholder is 16px at 10px padding, but every applied
|
|
13
|
+
* instance and the medallion spec sheet itself use 20px at 8px — the file is
|
|
14
|
+
* inconsistent here, and the 20/8 pairing is the one that fills the tile.
|
|
15
|
+
*
|
|
16
|
+
* There is deliberately no unframed variant — the spec never shows a bare glyph,
|
|
17
|
+
* at either scale, so an illustration always carries the tile.
|
|
18
|
+
*
|
|
19
|
+
* `rounded-[15px]` is off the radius scale on purpose: it's the value Figma
|
|
20
|
+
* reports for this node, and fitting a superellipse to the rendered outline
|
|
21
|
+
* agrees — a plain circular corner at r=14.5 matches to 0.29px, and r=15 to
|
|
22
|
+
* 0.35px, while the nearest token (16) is off by 0.73px. There is no corner
|
|
23
|
+
* smoothing on this shape, so no `corner-shape` is needed.
|
|
24
|
+
*/
|
|
25
|
+
export declare const emptyStateIllustrationVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
26
|
+
export declare const emptyStateMessageVariants: (props?: ({
|
|
27
|
+
type?: "collection-empty" | "no-results" | null | undefined;
|
|
28
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
29
|
+
export declare const emptyStateTitleVariants: (props?: ({
|
|
30
|
+
type?: "collection-empty" | "no-results" | null | undefined;
|
|
31
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
@@ -1,13 +1,36 @@
|
|
|
1
1
|
import { cva } from "class-variance-authority";
|
|
2
|
-
const emptyStateVariants = cva('flex flex-col items-center text-center
|
|
2
|
+
const emptyStateVariants = cva('flex flex-col items-center text-center m-[0_auto]', {
|
|
3
3
|
variants: {
|
|
4
4
|
type: {
|
|
5
|
-
'collection-empty': 'max-w-[560px] min-w-[256px]',
|
|
6
|
-
'no-results': 'w-[240px]
|
|
5
|
+
'collection-empty': 'gap-16 py-24 max-w-[560px] min-w-[256px]',
|
|
6
|
+
'no-results': 'gap-8 py-8 w-[240px]'
|
|
7
7
|
}
|
|
8
8
|
},
|
|
9
9
|
defaultVariants: {
|
|
10
10
|
type: 'collection-empty'
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
|
-
|
|
13
|
+
const emptyStateIllustrationVariants = cva('empty-state-medallion flex items-center justify-center shrink-0 size-36 rounded-[15px] p-8 text-xl text-icon-secondary');
|
|
14
|
+
const emptyStateMessageVariants = cva('flex flex-col items-center', {
|
|
15
|
+
variants: {
|
|
16
|
+
type: {
|
|
17
|
+
'collection-empty': 'gap-8',
|
|
18
|
+
'no-results': 'gap-4'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
type: 'collection-empty'
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const emptyStateTitleVariants = cva('text-center break-words', {
|
|
26
|
+
variants: {
|
|
27
|
+
type: {
|
|
28
|
+
'collection-empty': 'font-pixel text-base leading-base text-text-primary',
|
|
29
|
+
'no-results': 'font-sans-display text-sm font-medium text-text-secondary'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
type: 'collection-empty'
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
export { emptyStateIllustrationVariants, emptyStateMessageVariants, emptyStateTitleVariants, emptyStateVariants };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { SvgIcon } from "./SvgIcon.js";
|
|
3
|
+
const Shapes = (props)=>/*#__PURE__*/ jsx(SvgIcon, {
|
|
4
|
+
...props,
|
|
5
|
+
viewBox: "0 0 24 24",
|
|
6
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
7
|
+
d: "M9 13C10.105 13 11 13.895 11 15V20C11 21.105 10.105 22 9 22H4C2.895 22 2 21.105 2 20V15C2 13.895 2.895 13 4 13H9ZM17.5 13C19.985 13 22 15.015 22 17.5C22 19.985 19.985 22 17.5 22C15.015 22 13 19.985 13 17.5C13 15.015 15.015 13 17.5 13ZM4 20H9V15H4V20ZM17.5 15C16.119 15 15 16.119 15 17.5C15 18.881 16.119 20 17.5 20C18.881 20 20 18.881 20 17.5C20 16.119 18.881 15 17.5 15ZM12.169 1.647C12.314 1.661 12.457 1.692 12.594 1.742L12.794 1.83L12.981 1.943C13.161 2.068 13.315 2.226 13.435 2.41C13.438 2.416 13.443 2.422 13.446 2.428L17.148 8.371C17.287 8.584 17.377 8.824 17.41 9.075L17.422 9.19L17.425 9.307C17.424 9.577 17.358 9.843 17.233 10.084C17.09 10.359 16.875 10.59 16.61 10.751C16.346 10.912 16.041 10.999 15.731 11H8.3V10.999C8.005 11.006 7.713 10.937 7.453 10.797C7.178 10.648 6.948 10.425 6.791 10.154C6.634 9.883 6.555 9.573 6.562 9.26C6.57 8.946 6.664 8.641 6.834 8.378L10.554 2.467L10.555 2.469C10.695 2.233 10.889 2.034 11.123 1.891C11.373 1.738 11.658 1.651 11.95 1.641L12.169 1.647ZM8.806 9H15.184L12.011 3.905L8.806 9Z",
|
|
8
|
+
fill: "currentColor"
|
|
9
|
+
})
|
|
10
|
+
});
|
|
11
|
+
Shapes.displayName = 'ShapesIcon';
|
|
12
|
+
export { Shapes };
|
package/dist/icons/index.d.ts
CHANGED
|
@@ -438,6 +438,7 @@ export { Ses } from './Ses';
|
|
|
438
438
|
export { SesNeutral } from './SesNeutral';
|
|
439
439
|
export { Settings } from './Settings';
|
|
440
440
|
export { Settings2 } from './Settings2';
|
|
441
|
+
export { Shapes } from './Shapes';
|
|
441
442
|
export { Share } from './Share';
|
|
442
443
|
export { Sheet } from './Sheet';
|
|
443
444
|
export { Shield } from './Shield';
|
package/dist/icons/index.js
CHANGED
|
@@ -438,6 +438,7 @@ export { Ses } from "./Ses.js";
|
|
|
438
438
|
export { SesNeutral } from "./SesNeutral.js";
|
|
439
439
|
export { Settings } from "./Settings.js";
|
|
440
440
|
export { Settings2 } from "./Settings2.js";
|
|
441
|
+
export { Shapes } from "./Shapes.js";
|
|
441
442
|
export { Share } from "./Share.js";
|
|
442
443
|
export { Sheet } from "./Sheet.js";
|
|
443
444
|
export { Shield } from "./Shield.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.6.
|
|
3
|
-
"generatedAt": "2026-08-
|
|
2
|
+
"version": "1.6.2",
|
|
3
|
+
"generatedAt": "2026-08-25T16:40:44.467Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Accordion",
|
|
@@ -29733,19 +29733,26 @@
|
|
|
29733
29733
|
"examples": [
|
|
29734
29734
|
{
|
|
29735
29735
|
"name": "CollectionEmpty",
|
|
29736
|
-
"code": "args => (\n <EmptyState {...args}>\n <EmptyStateIllustration>\n <
|
|
29736
|
+
"code": "args => (\n <EmptyState {...args}>\n <EmptyStateIllustration>\n <Shapes />\n </EmptyStateIllustration>\n <EmptyStateMessage>\n <EmptyStateTitle>Title text goes here</EmptyStateTitle>\n <EmptyStateDescription>\n Description text explains what happened and what the user can do next.\n <br />\n One to two sentences maximum.\n </EmptyStateDescription>\n </EmptyStateMessage>\n <EmptyStateActions>\n <Button size='medium'>Reset filters</Button>\n <Button size='medium' variant='outline' color='neutral'>\n Refresh\n </Button>\n </EmptyStateActions>\n <EmptyStateLink href='#'>Link</EmptyStateLink>\n </EmptyState>\n)"
|
|
29737
29737
|
},
|
|
29738
29738
|
{
|
|
29739
|
-
"name": "
|
|
29740
|
-
"code": "args => (\n <EmptyState {...args}>\n <EmptyStateIllustration>\n <Zap
|
|
29739
|
+
"name": "NoFilterResults",
|
|
29740
|
+
"code": "args => (\n <EmptyState {...args}>\n <EmptyStateIllustration>\n <Zap />\n </EmptyStateIllustration>\n <EmptyStateMessage>\n <EmptyStateTitle>No attacks found</EmptyStateTitle>\n <EmptyStateDescription>Try to apply different filter or reset it.</EmptyStateDescription>\n </EmptyStateMessage>\n <EmptyStateActions>\n <Button size='medium' variant='outline' color='neutral'>\n Refresh\n </Button>\n </EmptyStateActions>\n </EmptyState>\n)",
|
|
29741
|
+
"description": "The page-level state for a search or filter that matched nothing. Same\n`collection-empty` scale as an untouched collection — the difference is the\nCTA: a neutral \"get back to something\" action, never a create action, because\nthe data exists and the query is what needs changing."
|
|
29741
29742
|
},
|
|
29742
29743
|
{
|
|
29743
29744
|
"name": "Minimal",
|
|
29744
|
-
"code": "args => (\n <EmptyState {...args}>\n <EmptyStateMessage>\n <EmptyStateTitle>
|
|
29745
|
+
"code": "args => (\n <EmptyState {...args}>\n <EmptyStateMessage>\n <EmptyStateTitle>No results</EmptyStateTitle>\n <EmptyStateDescription>Short description</EmptyStateDescription>\n </EmptyStateMessage>\n </EmptyState>\n)",
|
|
29746
|
+
"description": "The compact state at its spec default: no icon, no actions — just the title\nand subtitle. This is the shape most in-app empty states take."
|
|
29745
29747
|
},
|
|
29746
29748
|
{
|
|
29747
29749
|
"name": "NoResultsExamples",
|
|
29748
29750
|
"code": "() => (\n <HStack gap={32} align='start'>\n {/* In a chart card */}\n <Card className='w-[340px]'>\n <CardHeader>\n <CardTitle>Bar chart</CardTitle>\n </CardHeader>\n <CardContent className='flex items-center justify-center py-48'>\n <EmptyState type='no-results'>\n <EmptyStateMessage>\n <EmptyStateDescription>No data found for selected period</EmptyStateDescription>\n </EmptyStateMessage>\n </EmptyState>\n </CardContent>\n </Card>\n\n {/* In a select dropdown */}\n <VStack gap={4} className='w-[260px]'>\n <Field>\n <FieldLabel>Label</FieldLabel>\n <Select collection={emptyCollection}>\n <SelectButton placeholder='Select' />\n <SelectPositioner className='w-[260px]'>\n <SelectContent />\n </SelectPositioner>\n </Select>\n </Field>\n </VStack>\n </HStack>\n)"
|
|
29751
|
+
},
|
|
29752
|
+
{
|
|
29753
|
+
"name": "Medallion",
|
|
29754
|
+
"code": "() => (\n <HStack gap={0} className='w-[330px] overflow-hidden rounded-16 border border-border-primary'>\n {(['bg-bg-surface-1', 'bg-bg-light-primary'] as const).map(surface => (\n <div key={surface} className={cn('flex flex-1 justify-center py-28', surface)}>\n <EmptyStateIllustration>\n <ScanLine />\n </EmptyStateIllustration>\n </div>\n ))}\n </HStack>\n)",
|
|
29755
|
+
"description": "The medallion reads as a lit surface: a top-down gradient, a hairline border,\na soft drop shadow and an inner top highlight. Every layer resolves through\n`--color-component-empty-state-medallion-*` and\n`--shadow-empty-state-medallion`, so it holds up on both the card surface and\nthe slightly darker page surface it sits on — shown here side by side. Use the\ntoolbar theme switcher to check it in dark."
|
|
29749
29756
|
}
|
|
29750
29757
|
]
|
|
29751
29758
|
},
|
|
@@ -82645,13 +82652,13 @@
|
|
|
82645
82652
|
},
|
|
82646
82653
|
{
|
|
82647
82654
|
"name": "EmptyCollection",
|
|
82648
|
-
"code": "() => (\n <Table data={[]} columns={securityColumns} getRowId={row => row.id}>\n <TableEmptyState>\n <EmptyState type='collection-empty'>\n <EmptyStateIllustration>\n <Database
|
|
82655
|
+
"code": "() => (\n <Table data={[]} columns={securityColumns} getRowId={row => row.id}>\n <TableEmptyState>\n <EmptyState type='collection-empty'>\n <EmptyStateIllustration>\n <Database />\n </EmptyStateIllustration>\n <EmptyStateMessage>\n <EmptyStateTitle>No events yet</EmptyStateTitle>\n <EmptyStateDescription>\n Security events will appear here as soon as Wallarm detects traffic.\n </EmptyStateDescription>\n </EmptyStateMessage>\n <EmptyStateActions>\n <Button size='medium'>Configure source</Button>\n </EmptyStateActions>\n </EmptyState>\n </TableEmptyState>\n </Table>\n)",
|
|
82649
82656
|
"description": "When `data` is empty and the table is not loading, the `TableEmptyState` slot\nrenders its children. Compose the `EmptyState` component inside it to get the\nstandard illustration / message / actions layout.\n\nUse `type='collection-empty'` when there is genuinely no data yet (nothing has\nbeen created), and offer a primary action that creates the first item."
|
|
82650
82657
|
},
|
|
82651
82658
|
{
|
|
82652
82659
|
"name": "NoResults",
|
|
82653
|
-
"code": "() => (\n <Table data={[]} columns={securityColumns} getRowId={row => row.id}>\n <TableEmptyState>\n <EmptyState type='
|
|
82654
|
-
"description": "
|
|
82660
|
+
"code": "() => (\n <Table data={[]} columns={securityColumns} getRowId={row => row.id}>\n <TableEmptyState>\n <EmptyState type='collection-empty'>\n <EmptyStateIllustration>\n <SearchX />\n </EmptyStateIllustration>\n <EmptyStateMessage>\n <EmptyStateTitle>No results found</EmptyStateTitle>\n <EmptyStateDescription>\n No events match your current filters. Try adjusting or resetting them.\n </EmptyStateDescription>\n </EmptyStateMessage>\n <EmptyStateActions>\n <Button size='medium' variant='outline' color='neutral'>\n <FilterX />\n Reset filters\n </Button>\n </EmptyStateActions>\n </EmptyState>\n </TableEmptyState>\n </Table>\n)",
|
|
82661
|
+
"description": "When data exists but the current filters or search matched nothing, stay at\nthe `collection-empty` scale — a table body is a page-level surface, and\n`no-results` is the compact treatment for a card or menu. What changes is the\naction: a neutral one that clears the filters, never a primary \"create\"."
|
|
82655
82662
|
},
|
|
82656
82663
|
{
|
|
82657
82664
|
"name": "ColumnResizing",
|
|
@@ -95889,6 +95896,18 @@
|
|
|
95889
95896
|
"name": "--color-component-app-shell-bg",
|
|
95890
95897
|
"value": "var(--color-slate-100)"
|
|
95891
95898
|
},
|
|
95899
|
+
{
|
|
95900
|
+
"name": "--color-component-empty-state-medallion-bg-from",
|
|
95901
|
+
"value": "var(--color-white)"
|
|
95902
|
+
},
|
|
95903
|
+
{
|
|
95904
|
+
"name": "--color-component-empty-state-medallion-bg-to",
|
|
95905
|
+
"value": "--alpha(var(--color-slate-100) / 80%)"
|
|
95906
|
+
},
|
|
95907
|
+
{
|
|
95908
|
+
"name": "--color-component-empty-state-medallion-border",
|
|
95909
|
+
"value": "--alpha(var(--color-slate-300) / 70%)"
|
|
95910
|
+
},
|
|
95892
95911
|
{
|
|
95893
95912
|
"name": "--color-syntax-no-syntax",
|
|
95894
95913
|
"value": "var(--color-slate-900)"
|
|
@@ -96951,6 +96970,18 @@
|
|
|
96951
96970
|
"name": "--color-component-app-shell-bg",
|
|
96952
96971
|
"value": "var(--color-slate-975)"
|
|
96953
96972
|
},
|
|
96973
|
+
{
|
|
96974
|
+
"name": "--color-component-empty-state-medallion-bg-from",
|
|
96975
|
+
"value": "--alpha(var(--color-white) / 7%)"
|
|
96976
|
+
},
|
|
96977
|
+
{
|
|
96978
|
+
"name": "--color-component-empty-state-medallion-bg-to",
|
|
96979
|
+
"value": "--alpha(var(--color-white) / 2%)"
|
|
96980
|
+
},
|
|
96981
|
+
{
|
|
96982
|
+
"name": "--color-component-empty-state-medallion-border",
|
|
96983
|
+
"value": "--alpha(var(--color-white) / 13%)"
|
|
96984
|
+
},
|
|
96954
96985
|
{
|
|
96955
96986
|
"name": "--color-syntax-no-syntax",
|
|
96956
96987
|
"value": "var(--color-slate-200)"
|
|
@@ -4,7 +4,7 @@ import { Text } from '../components/Text';
|
|
|
4
4
|
|
|
5
5
|
const DESCRIPTION = [
|
|
6
6
|
'Geist Pixel is the decorative typeface. Use it for empty states, and for moments that want personality rather than information.',
|
|
7
|
-
'There is no `Pixel` component. Pixel type is composed from the `font-pixel` utility plus the shared `text-*` size utilities, so the steps below are conventions rather than a component API. The only place it ships today is `EmptyStateTitle
|
|
7
|
+
'There is no `Pixel` component. Pixel type is composed from the `font-pixel` utility plus the shared `text-*` size utilities, so the steps below are conventions rather than a component API. The only place it ships today is `EmptyStateTitle` at the `collection-empty` scale, at 16/24 — the compact `no-results` title is sans, not pixel.',
|
|
8
8
|
'One cut only: Square at regular weight. There is no light or bold, so `font-bold` on pixel type renders as a browser-synthesized fake — emphasis has to come from size or colour, never from weight.',
|
|
9
9
|
'Not for UI copy, labels, or anything read at length; that is the text ramp. Keep it to short strings, where the blocky letterforms stay legible.',
|
|
10
10
|
].join('\n\n');
|
package/dist/theme/index.css
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
@import './icon.css';
|
|
18
18
|
@import './utilities/hover-overlay.css';
|
|
19
19
|
@import './utilities/code-snippet-bg.css';
|
|
20
|
+
@import './utilities/empty-state-medallion.css';
|
|
20
21
|
@import './components/drawer.css';
|
|
21
22
|
@import './components/dialog.css';
|
|
22
23
|
@import './components/tooltip.css';
|
package/dist/theme/semantic.css
CHANGED
|
@@ -216,6 +216,24 @@
|
|
|
216
216
|
--color-component-toast-ic-info: var(--color-blue-400);
|
|
217
217
|
--color-component-app-shell-bg: var(--color-slate-100);
|
|
218
218
|
|
|
219
|
+
/* EmptyState medallion — the small raised tile behind the empty-state icon.
|
|
220
|
+
* Reads as a lit surface: a top-down gradient, a hairline border, a soft
|
|
221
|
+
* drop shadow and an inner top highlight. Kept as tokens (not utilities)
|
|
222
|
+
* because every layer flips between light and dark. */
|
|
223
|
+
--color-component-empty-state-medallion-bg-from: var(--color-white);
|
|
224
|
+
--color-component-empty-state-medallion-bg-to: --alpha(var(--color-slate-100) / 80%);
|
|
225
|
+
--color-component-empty-state-medallion-border: --alpha(var(--color-slate-300) / 70%);
|
|
226
|
+
/* The hairline rides in the shadow list as an inset ring rather than a real
|
|
227
|
+
* border: Figma composites the top highlight *over* the stroke, and a CSS
|
|
228
|
+
* inset shadow is clipped to the padding box, so it can never reach a real
|
|
229
|
+
* border. Ordering matters — earlier shadows paint on top, so the highlight
|
|
230
|
+
* and the inner shade both land over the ring, as in Figma. It also keeps
|
|
231
|
+
* the glyph at 8px from the edge instead of 9px. */
|
|
232
|
+
--shadow-empty-state-medallion:
|
|
233
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.45), inset 0 -1px 1px 0 rgba(15, 23, 43, 0.04),
|
|
234
|
+
inset 0 0 0 1px var(--color-component-empty-state-medallion-border),
|
|
235
|
+
0 2px 2.5px 0 rgba(15, 23, 43, 0.03), 0 1px 1px 0 rgba(15, 23, 43, 0.05);
|
|
236
|
+
|
|
219
237
|
/* ========================================
|
|
220
238
|
* SYNTAX COLORS
|
|
221
239
|
* ⚠️ CODE-MANAGED — DO NOT SYNC FROM FIGMA
|
|
@@ -612,6 +630,16 @@
|
|
|
612
630
|
--color-component-toast-ic-info: var(--color-blue-600);
|
|
613
631
|
--color-component-app-shell-bg: var(--color-slate-975);
|
|
614
632
|
|
|
633
|
+
/* EmptyState medallion (dark) — same recipe inverted: the gradient and the
|
|
634
|
+
* top highlight become low-alpha white, the shadows deepen to near-black. */
|
|
635
|
+
--color-component-empty-state-medallion-bg-from: --alpha(var(--color-white) / 7%);
|
|
636
|
+
--color-component-empty-state-medallion-bg-to: --alpha(var(--color-white) / 2%);
|
|
637
|
+
--color-component-empty-state-medallion-border: --alpha(var(--color-white) / 13%);
|
|
638
|
+
--shadow-empty-state-medallion:
|
|
639
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.05), inset 0 -1px 2px 0 rgba(0, 0, 0, 0.16),
|
|
640
|
+
inset 0 0 0 1px var(--color-component-empty-state-medallion-border),
|
|
641
|
+
0 2px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 2px 0 rgba(0, 0, 0, 0.24);
|
|
642
|
+
|
|
615
643
|
/* ========================================
|
|
616
644
|
* SYNTAX COLORS
|
|
617
645
|
* ⚠️ CODE-MANAGED — DO NOT SYNC FROM FIGMA
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The raised tile behind an EmptyState icon.
|
|
3
|
+
*
|
|
4
|
+
* Composed here rather than in the component so the three layers Figma draws
|
|
5
|
+
* as stacked overlays (gradient fill, outer drop shadow, inner top highlight)
|
|
6
|
+
* collapse into one element: the gradient is a background-image, and the drop
|
|
7
|
+
* shadow, the inset highlight and the hairline ring all ride in a single
|
|
8
|
+
* box-shadow list. All of it resolves through tokens that flip in
|
|
9
|
+
* `[data-theme='dark']`.
|
|
10
|
+
*/
|
|
11
|
+
@utility empty-state-medallion {
|
|
12
|
+
background-image: linear-gradient(
|
|
13
|
+
to bottom,
|
|
14
|
+
var(--color-component-empty-state-medallion-bg-from),
|
|
15
|
+
var(--color-component-empty-state-medallion-bg-to)
|
|
16
|
+
);
|
|
17
|
+
box-shadow: var(--shadow-empty-state-medallion);
|
|
18
|
+
}
|