@calcifer-design/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/components/Button/Button.d.ts +12 -0
  2. package/dist/components/Button/Button.js +26 -0
  3. package/dist/components/Button/Button.module.js +6 -0
  4. package/dist/components/Button/Button_module.css +79 -0
  5. package/dist/components/Button/LinkButton.d.ts +19 -0
  6. package/dist/components/Button/LinkButton.js +24 -0
  7. package/dist/components/Card/Card.d.ts +15 -0
  8. package/dist/components/Card/Card.js +31 -0
  9. package/dist/components/Card/Card.module.js +9 -0
  10. package/dist/components/Card/Card_module.css +55 -0
  11. package/dist/components/DataTable/DataTable.d.ts +46 -0
  12. package/dist/components/DataTable/DataTable.js +98 -0
  13. package/dist/components/DataTable/DataTable.module.js +13 -0
  14. package/dist/components/DataTable/DataTable_module.css +149 -0
  15. package/dist/components/LiveRegion/LiveRegion.d.ts +5 -0
  16. package/dist/components/LiveRegion/LiveRegion.js +13 -0
  17. package/dist/components/LiveRegion/LiveRegion.module.js +5 -0
  18. package/dist/components/LiveRegion/LiveRegion_module.css +12 -0
  19. package/dist/components/NavMenu/NavMenu.d.ts +19 -0
  20. package/dist/components/NavMenu/NavMenu.js +86 -0
  21. package/dist/components/NavMenu/NavMenu.module.js +12 -0
  22. package/dist/components/NavMenu/NavMenu_module.css +85 -0
  23. package/dist/components/PageHeading/PageHeading.d.ts +16 -0
  24. package/dist/components/PageHeading/PageHeading.js +32 -0
  25. package/dist/components/PageHeading/PageHeading.module.js +7 -0
  26. package/dist/components/PageHeading/PageHeading_module.css +53 -0
  27. package/dist/components/SkipLink/SkipLink.d.ts +6 -0
  28. package/dist/components/SkipLink/SkipLink.js +11 -0
  29. package/dist/components/SkipLink/SkipLink.module.js +5 -0
  30. package/dist/components/SkipLink/SkipLink_module.css +24 -0
  31. package/dist/components/StatusDot/StatusDot.d.ts +17 -0
  32. package/dist/components/StatusDot/StatusDot.js +26 -0
  33. package/dist/components/StatusDot/StatusDot.module.js +9 -0
  34. package/dist/components/StatusDot/StatusDot_module.css +73 -0
  35. package/dist/components/Tabs/Tabs.d.ts +16 -0
  36. package/dist/components/Tabs/Tabs.js +35 -0
  37. package/dist/components/Tabs/Tabs.module.js +8 -0
  38. package/dist/components/Tabs/Tabs_module.css +53 -0
  39. package/dist/components/Tag/Tag.d.ts +6 -0
  40. package/dist/components/Tag/Tag.js +10 -0
  41. package/dist/components/Tag/Tag.module.js +5 -0
  42. package/dist/components/Tag/Tag_module.css +20 -0
  43. package/dist/hooks/useMediaQuery.d.ts +3 -0
  44. package/dist/hooks/useMediaQuery.js +22 -0
  45. package/dist/index.d.ts +23 -0
  46. package/dist/index.js +12 -0
  47. package/dist/styles/base.css +58 -0
  48. package/package.json +69 -0
@@ -0,0 +1,86 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Menu } from "@base-ui/react/menu";
3
+ import { useState } from "react";
4
+ import { minWidth, useMediaQuery } from "../../hooks/useMediaQuery.js";
5
+ import NavMenu_module from "./NavMenu.module.js";
6
+ function defaultRenderLink(item, { children, ...linkProps }) {
7
+ return /*#__PURE__*/ jsx("a", {
8
+ href: item.href,
9
+ ...linkProps,
10
+ children: children
11
+ });
12
+ }
13
+ function isCurrent(item, currentPath) {
14
+ if ('/' === item.href) return '/' === currentPath;
15
+ return currentPath === item.href || currentPath.startsWith(`${item.href}/`);
16
+ }
17
+ function linkPropsFor(item, currentPath) {
18
+ return {
19
+ className: NavMenu_module.link,
20
+ 'aria-current': isCurrent(item, currentPath) ? 'page' : void 0,
21
+ children: item.label
22
+ };
23
+ }
24
+ function NavMenu({ items, currentPath, label = 'Primary', renderLink = defaultRenderLink }) {
25
+ const isWide = useMediaQuery(minWidth('md'));
26
+ const [open, setOpen] = useState(false);
27
+ const [previousPath, setPreviousPath] = useState(currentPath);
28
+ if (currentPath !== previousPath) {
29
+ setPreviousPath(currentPath);
30
+ setOpen(false);
31
+ }
32
+ if (isWide) return /*#__PURE__*/ jsx("nav", {
33
+ className: NavMenu_module.root,
34
+ "aria-label": label,
35
+ children: /*#__PURE__*/ jsx("ul", {
36
+ className: NavMenu_module.list,
37
+ children: items.map((item)=>/*#__PURE__*/ jsx("li", {
38
+ children: renderLink(item, linkPropsFor(item, currentPath))
39
+ }, item.href))
40
+ })
41
+ });
42
+ return /*#__PURE__*/ jsx("nav", {
43
+ className: NavMenu_module.root,
44
+ "aria-label": label,
45
+ children: /*#__PURE__*/ jsxs(Menu.Root, {
46
+ open: open,
47
+ onOpenChange: setOpen,
48
+ modal: false,
49
+ children: [
50
+ /*#__PURE__*/ jsx(Menu.Trigger, {
51
+ className: NavMenu_module.trigger,
52
+ "aria-label": open ? 'Close menu' : 'Open menu',
53
+ children: /*#__PURE__*/ jsx("svg", {
54
+ className: NavMenu_module.triggerIcon,
55
+ viewBox: "0 0 18 18",
56
+ fill: "none",
57
+ stroke: "currentColor",
58
+ strokeWidth: "1.5",
59
+ strokeLinecap: "round",
60
+ "aria-hidden": "true",
61
+ focusable: "false",
62
+ children: /*#__PURE__*/ jsx("path", {
63
+ d: open ? 'M4 4l10 10M14 4L4 14' : 'M2 5h14M2 9h14M2 13h14'
64
+ })
65
+ })
66
+ }),
67
+ /*#__PURE__*/ jsx(Menu.Portal, {
68
+ children: /*#__PURE__*/ jsx(Menu.Positioner, {
69
+ className: NavMenu_module.positioner,
70
+ side: "bottom",
71
+ align: "end",
72
+ sideOffset: 8,
73
+ children: /*#__PURE__*/ jsx(Menu.Popup, {
74
+ className: NavMenu_module.popup,
75
+ children: items.map((item)=>/*#__PURE__*/ jsx(Menu.Item, {
76
+ className: NavMenu_module.item,
77
+ render: renderLink(item, linkPropsFor(item, currentPath))
78
+ }, item.href))
79
+ })
80
+ })
81
+ })
82
+ ]
83
+ })
84
+ });
85
+ }
86
+ export { NavMenu };
@@ -0,0 +1,12 @@
1
+ import "./NavMenu_module.css";
2
+ const NavMenu_module = {
3
+ root: "root-wsLiH",
4
+ list: "list-hzkjq",
5
+ link: "link-R0jaI",
6
+ trigger: "trigger-dj75u",
7
+ triggerIcon: "triggerIcon-gSo04",
8
+ positioner: "positioner-BPm1p",
9
+ popup: "popup-hUQFV",
10
+ item: "item-yXJoa"
11
+ };
12
+ export default NavMenu_module;
@@ -0,0 +1,85 @@
1
+ .root-wsLiH {
2
+ align-items: center;
3
+ display: flex;
4
+ }
5
+
6
+ .list-hzkjq {
7
+ align-items: center;
8
+ gap: var(--space-5);
9
+ margin: 0;
10
+ padding: 0;
11
+ list-style: none;
12
+ display: flex;
13
+ }
14
+
15
+ .link-R0jaI {
16
+ min-height: var(--size-touch);
17
+ border-radius: var(--radius-sm);
18
+ color: var(--color-text-muted);
19
+ font-size: var(--text-sm);
20
+ white-space: nowrap;
21
+ transition-property: color;
22
+ transition-duration: var(--motion-duration-1);
23
+ transition-timing-function: var(--motion-ease);
24
+ align-items: center;
25
+ font-weight: 400;
26
+ text-decoration: none;
27
+ display: inline-flex;
28
+ }
29
+
30
+ .link-R0jaI[aria-current="page"] {
31
+ color: var(--color-text);
32
+ font-weight: 500;
33
+ }
34
+
35
+ @media (hover: hover) {
36
+ .link-R0jaI:hover {
37
+ color: var(--color-text);
38
+ }
39
+ }
40
+
41
+ .trigger-dj75u {
42
+ width: var(--size-touch);
43
+ height: var(--size-touch);
44
+ border-radius: var(--radius-md);
45
+ color: var(--color-text);
46
+ cursor: pointer;
47
+ background: none;
48
+ border: 0;
49
+ justify-content: center;
50
+ align-items: center;
51
+ padding: 0;
52
+ display: inline-flex;
53
+ }
54
+
55
+ .triggerIcon-gSo04 {
56
+ width: 1.125rem;
57
+ height: 1.125rem;
58
+ }
59
+
60
+ .positioner-BPm1p {
61
+ z-index: 50;
62
+ }
63
+
64
+ .popup-hUQFV {
65
+ min-width: 12rem;
66
+ padding: var(--space-2);
67
+ border: 1px solid var(--color-border);
68
+ border-radius: var(--radius-lg);
69
+ background: var(--color-surface-raised);
70
+ box-shadow: var(--elevation-2);
71
+ flex-direction: column;
72
+ display: flex;
73
+ }
74
+
75
+ .item-yXJoa {
76
+ padding-inline: var(--space-3);
77
+ border-radius: var(--radius-md);
78
+ font-size: var(--text-md);
79
+ }
80
+
81
+ .item-yXJoa[data-highlighted] {
82
+ background: var(--color-surface-2);
83
+ color: var(--color-text);
84
+ }
85
+
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from 'react';
2
+ export type PageHeadingSize = 'display' | 'page' | 'compact';
3
+ export interface PageHeadingProps {
4
+ children: ReactNode;
5
+ eyebrow?: ReactNode;
6
+ level?: 1 | 2;
7
+ /**
8
+ * The mock's three h1 sizes: `display` is the landing hero name, `page` (default) the
9
+ * Projects/About titles, `compact` the project and Under-the-hood titles. Ignored for
10
+ * `level={2}`, which always renders the section size.
11
+ */
12
+ size?: PageHeadingSize;
13
+ /** Move focus to the heading when it mounts (used after client-side navigation). */
14
+ focusOnMount?: boolean;
15
+ }
16
+ export declare function PageHeading({ children, eyebrow, level, size, focusOnMount, }: PageHeadingProps): import("react").JSX.Element;
@@ -0,0 +1,32 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
3
+ import PageHeading_module from "./PageHeading.module.js";
4
+ function PageHeading({ children, eyebrow, level = 1, size = 'page', focusOnMount = false }) {
5
+ const headingRef = useRef(null);
6
+ const Heading = 1 === level ? 'h1' : 'h2';
7
+ useEffect(()=>{
8
+ if (focusOnMount) headingRef.current?.focus({
9
+ preventScroll: false
10
+ });
11
+ }, [
12
+ focusOnMount
13
+ ]);
14
+ return /*#__PURE__*/ jsxs("div", {
15
+ className: PageHeading_module.root,
16
+ children: [
17
+ eyebrow ? /*#__PURE__*/ jsx("p", {
18
+ className: PageHeading_module.eyebrow,
19
+ children: eyebrow
20
+ }) : null,
21
+ /*#__PURE__*/ jsx(Heading, {
22
+ ref: headingRef,
23
+ tabIndex: -1,
24
+ className: PageHeading_module.heading,
25
+ "data-level": level,
26
+ "data-size": size,
27
+ children: children
28
+ })
29
+ ]
30
+ });
31
+ }
32
+ export { PageHeading };
@@ -0,0 +1,7 @@
1
+ import "./PageHeading_module.css";
2
+ const PageHeading_module = {
3
+ root: "root-G6df9",
4
+ eyebrow: "eyebrow-N5iEz",
5
+ heading: "heading-C4QBU"
6
+ };
7
+ export default PageHeading_module;
@@ -0,0 +1,53 @@
1
+ .root-G6df9 {
2
+ gap: var(--space-2);
3
+ flex-direction: column;
4
+ display: flex;
5
+ }
6
+
7
+ .eyebrow-N5iEz {
8
+ font-family: var(--font-mono);
9
+ font-size: var(--text-sm);
10
+ color: var(--color-text-muted);
11
+ text-transform: uppercase;
12
+ letter-spacing: var(--tracking-wide);
13
+ }
14
+
15
+ .heading-C4QBU {
16
+ font-size: var(--text-h1);
17
+ letter-spacing: var(--tracking-h1);
18
+ font-weight: 600;
19
+ line-height: var(--leading-display);
20
+ text-wrap: balance;
21
+ outline-offset: var(--space-2);
22
+ }
23
+
24
+ .heading-C4QBU[data-size="display"] {
25
+ max-width: 12ch;
26
+ font-size: var(--text-display);
27
+ letter-spacing: var(--tracking-display);
28
+ line-height: var(--leading-display);
29
+ }
30
+
31
+ @media (width >= 112.5rem) {
32
+ .heading-C4QBU[data-size="display"] {
33
+ font-size: var(--text-display-wide);
34
+ }
35
+ }
36
+
37
+ .heading-C4QBU[data-size="compact"] {
38
+ font-size: var(--text-h2);
39
+ letter-spacing: var(--tracking-heading);
40
+ line-height: var(--leading-tight);
41
+ }
42
+
43
+ .heading-C4QBU[data-level="2"] {
44
+ max-width: none;
45
+ font-size: var(--text-h2-feature);
46
+ letter-spacing: var(--tracking-heading);
47
+ line-height: var(--leading-tight);
48
+ }
49
+
50
+ .heading-C4QBU:focus:not(:focus-visible) {
51
+ outline: none;
52
+ }
53
+
@@ -0,0 +1,6 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface SkipLinkProps {
3
+ targetId: string;
4
+ children?: ReactNode;
5
+ }
6
+ export declare function SkipLink({ targetId, children }: SkipLinkProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import SkipLink_module from "./SkipLink.module.js";
3
+ function SkipLink({ targetId, children = 'Skip to content' }) {
4
+ return /*#__PURE__*/ jsx("a", {
5
+ className: SkipLink_module.root,
6
+ href: `#${targetId}`,
7
+ onClick: ()=>document.getElementById(targetId)?.focus(),
8
+ children: children
9
+ });
10
+ }
11
+ export { SkipLink };
@@ -0,0 +1,5 @@
1
+ import "./SkipLink_module.css";
2
+ const SkipLink_module = {
3
+ root: "root-arteG"
4
+ };
5
+ export default SkipLink_module;
@@ -0,0 +1,24 @@
1
+ .root-arteG {
2
+ min-height: var(--size-touch);
3
+ top: var(--space-2);
4
+ left: var(--space-2);
5
+ z-index: 100;
6
+ padding: var(--space-2) var(--space-4);
7
+ border-radius: var(--radius-md);
8
+ background: var(--color-accent);
9
+ color: var(--color-accent-text);
10
+ transition-property: transform;
11
+ transition-duration: var(--motion-duration-1);
12
+ transition-timing-function: var(--motion-ease);
13
+ align-items: center;
14
+ font-weight: 600;
15
+ text-decoration: none;
16
+ display: inline-flex;
17
+ position: absolute;
18
+ transform: translateY(-200%);
19
+ }
20
+
21
+ .root-arteG:focus-visible {
22
+ transform: translateY(0);
23
+ }
24
+
@@ -0,0 +1,17 @@
1
+ export type RemoteStatus = 'registered' | 'loading' | 'loaded' | 'failed';
2
+ export interface StatusDotProps {
3
+ status: RemoteStatus;
4
+ /** The thing whose status this is, e.g. the remote name. */
5
+ label: string;
6
+ /**
7
+ * `mono` (default) sets the label in the monospace face, as in tables and badges; `text`
8
+ * sets it in the body face and text colour, as in the landing page's fleet list.
9
+ */
10
+ labelStyle?: 'mono' | 'text';
11
+ /**
12
+ * `false` keeps the label for assistive tech but hides it visually — for a table whose
13
+ * first column already names the remote.
14
+ */
15
+ showLabel?: boolean;
16
+ }
17
+ export declare function StatusDot({ status, label, labelStyle, showLabel, }: StatusDotProps): import("react").JSX.Element;
@@ -0,0 +1,26 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import StatusDot_module from "./StatusDot.module.js";
3
+ function StatusDot({ status, label, labelStyle = 'mono', showLabel = true }) {
4
+ return /*#__PURE__*/ jsxs("span", {
5
+ className: StatusDot_module.root,
6
+ "data-status": status,
7
+ "data-label-style": labelStyle,
8
+ "data-testid": "status-dot",
9
+ children: [
10
+ /*#__PURE__*/ jsx("span", {
11
+ className: StatusDot_module.dot,
12
+ "aria-hidden": "true"
13
+ }),
14
+ /*#__PURE__*/ jsx("span", {
15
+ className: StatusDot_module.label,
16
+ "data-hidden": showLabel ? void 0 : '',
17
+ children: label
18
+ }),
19
+ /*#__PURE__*/ jsx("span", {
20
+ className: StatusDot_module.status,
21
+ children: status
22
+ })
23
+ ]
24
+ });
25
+ }
26
+ export { StatusDot };
@@ -0,0 +1,9 @@
1
+ import "./StatusDot_module.css";
2
+ const StatusDot_module = {
3
+ root: "root-tgMEi",
4
+ dot: "dot-WDZDf",
5
+ pulse: "pulse-RqPMg",
6
+ label: "label-m8svl",
7
+ status: "status-t1WpV"
8
+ };
9
+ export default StatusDot_module;
@@ -0,0 +1,73 @@
1
+ .root-tgMEi {
2
+ align-items: center;
3
+ gap: var(--space-2);
4
+ min-width: 0;
5
+ font-size: var(--text-sm);
6
+ display: inline-flex;
7
+ }
8
+
9
+ .dot-WDZDf {
10
+ border-radius: var(--radius-full);
11
+ background: var(--color-neutral);
12
+ flex: none;
13
+ width: .5rem;
14
+ height: .5rem;
15
+ }
16
+
17
+ .root-tgMEi[data-status="loading"] .dot-WDZDf {
18
+ background: var(--color-accent);
19
+ animation: pulse-RqPMg var(--motion-duration-4) var(--motion-ease) infinite alternate;
20
+ }
21
+
22
+ .root-tgMEi[data-status="loaded"] .dot-WDZDf {
23
+ background: var(--color-success);
24
+ }
25
+
26
+ .root-tgMEi[data-status="failed"] .dot-WDZDf {
27
+ background: var(--color-danger);
28
+ }
29
+
30
+ .label-m8svl {
31
+ text-overflow: ellipsis;
32
+ white-space: nowrap;
33
+ min-width: 0;
34
+ font-family: var(--font-mono);
35
+ overflow: hidden;
36
+ }
37
+
38
+ .label-m8svl[data-hidden] {
39
+ clip-path: inset(50%);
40
+ white-space: nowrap;
41
+ border: 0;
42
+ width: 1px;
43
+ height: 1px;
44
+ margin: -1px;
45
+ padding: 0;
46
+ position: absolute;
47
+ overflow: hidden;
48
+ }
49
+
50
+ .root-tgMEi[data-label-style="text"] .label-m8svl {
51
+ font-family: var(--font-sans);
52
+ color: var(--color-text);
53
+ }
54
+
55
+ .status-t1WpV {
56
+ white-space: nowrap;
57
+ color: var(--color-text-muted);
58
+ }
59
+
60
+ .root-tgMEi[data-label-style="text"] .status-t1WpV {
61
+ color: var(--color-text-subtle);
62
+ }
63
+
64
+ @keyframes pulse-RqPMg {
65
+ from {
66
+ opacity: .35;
67
+ }
68
+
69
+ to {
70
+ opacity: 1;
71
+ }
72
+ }
73
+
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface TabItem {
3
+ value: string;
4
+ label: string;
5
+ content: ReactNode;
6
+ disabled?: boolean;
7
+ }
8
+ export interface TabsProps {
9
+ items: TabItem[];
10
+ /** Accessible name for the tab list. */
11
+ label: string;
12
+ value?: string;
13
+ defaultValue?: string;
14
+ onValueChange?: (value: string) => void;
15
+ }
16
+ export declare function Tabs({ items, label, value, defaultValue, onValueChange }: TabsProps): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Tabs } from "@base-ui/react/tabs";
3
+ import { useState } from "react";
4
+ import Tabs_module from "./Tabs.module.js";
5
+ function Tabs_Tabs({ items, label, value, defaultValue, onValueChange }) {
6
+ const [initialValue] = useState(()=>defaultValue ?? items[0]?.value);
7
+ return /*#__PURE__*/ jsxs(Tabs.Root, {
8
+ className: Tabs_module.root,
9
+ value: value,
10
+ defaultValue: initialValue,
11
+ onValueChange: (next)=>onValueChange?.(String(next)),
12
+ children: [
13
+ /*#__PURE__*/ jsxs(Tabs.List, {
14
+ className: Tabs_module.list,
15
+ "aria-label": label,
16
+ children: [
17
+ items.map((item)=>/*#__PURE__*/ jsx(Tabs.Tab, {
18
+ className: Tabs_module.tab,
19
+ value: item.value,
20
+ disabled: item.disabled,
21
+ children: item.label
22
+ }, item.value)),
23
+ /*#__PURE__*/ jsx(Tabs.Indicator, {
24
+ className: Tabs_module.indicator
25
+ })
26
+ ]
27
+ }),
28
+ items.map((item)=>/*#__PURE__*/ jsx(Tabs.Panel, {
29
+ value: item.value,
30
+ children: item.content
31
+ }, item.value))
32
+ ]
33
+ });
34
+ }
35
+ export { Tabs_Tabs as Tabs };
@@ -0,0 +1,8 @@
1
+ import "./Tabs_module.css";
2
+ const Tabs_module = {
3
+ root: "root-Hb4Mh",
4
+ list: "list-ZEK7A",
5
+ tab: "tab-FRO0R",
6
+ indicator: "indicator-a2Roe"
7
+ };
8
+ export default Tabs_module;
@@ -0,0 +1,53 @@
1
+ .root-Hb4Mh {
2
+ gap: var(--space-4);
3
+ flex-direction: column;
4
+ display: flex;
5
+ }
6
+
7
+ .list-ZEK7A {
8
+ gap: var(--space-1);
9
+ padding-block: var(--space-1);
10
+ border-bottom: 1px solid var(--color-border);
11
+ scrollbar-width: thin;
12
+ display: flex;
13
+ position: relative;
14
+ overflow-x: auto;
15
+ }
16
+
17
+ .tab-FRO0R {
18
+ min-height: var(--size-touch);
19
+ padding: var(--space-2) var(--space-3);
20
+ color: var(--color-text-muted);
21
+ font-size: var(--text-sm);
22
+ cursor: pointer;
23
+ transition-property: color;
24
+ transition-duration: var(--motion-duration-1);
25
+ transition-timing-function: var(--motion-ease);
26
+ background: none;
27
+ border: 0;
28
+ flex: none;
29
+ font-weight: 600;
30
+ }
31
+
32
+ .tab-FRO0R[data-active] {
33
+ color: var(--color-text);
34
+ }
35
+
36
+ .tab-FRO0R[data-disabled] {
37
+ color: var(--color-text-muted);
38
+ opacity: .5;
39
+ cursor: not-allowed;
40
+ }
41
+
42
+ .indicator-a2Roe {
43
+ bottom: -1px;
44
+ left: var(--active-tab-left);
45
+ width: var(--active-tab-width);
46
+ background: var(--color-accent);
47
+ height: 2px;
48
+ transition-property: left, width;
49
+ transition-duration: var(--motion-duration-2);
50
+ transition-timing-function: var(--motion-ease);
51
+ position: absolute;
52
+ }
53
+
@@ -0,0 +1,6 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface TagProps {
3
+ children: ReactNode;
4
+ tone?: 'neutral' | 'accent';
5
+ }
6
+ export declare function Tag({ children, tone }: TagProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import Tag_module from "./Tag.module.js";
3
+ function Tag({ children, tone = 'neutral' }) {
4
+ return /*#__PURE__*/ jsx("span", {
5
+ className: Tag_module.root,
6
+ "data-tone": tone,
7
+ children: children
8
+ });
9
+ }
10
+ export { Tag };
@@ -0,0 +1,5 @@
1
+ import "./Tag_module.css";
2
+ const Tag_module = {
3
+ root: "root-lzcr8"
4
+ };
5
+ export default Tag_module;
@@ -0,0 +1,20 @@
1
+ .root-lzcr8 {
2
+ min-height: 1.5rem;
3
+ padding: 0 var(--space-2);
4
+ border: 1px solid var(--color-border);
5
+ border-radius: var(--radius-full);
6
+ font-size: var(--text-xs);
7
+ line-height: var(--leading-snug);
8
+ white-space: nowrap;
9
+ background: var(--color-surface-2);
10
+ color: var(--color-text-muted);
11
+ align-items: center;
12
+ display: inline-flex;
13
+ }
14
+
15
+ .root-lzcr8[data-tone="accent"] {
16
+ background: var(--color-accent-soft);
17
+ border-color: var(--color-border-accent);
18
+ color: var(--color-text);
19
+ }
20
+
@@ -0,0 +1,3 @@
1
+ import { type BreakpointName } from '@calcifer-design/tokens';
2
+ export declare function minWidth(name: BreakpointName): string;
3
+ export declare function useMediaQuery(query: string): boolean;
@@ -0,0 +1,22 @@
1
+ import { useCallback, useSyncExternalStore } from "react";
2
+ import { breakpoint } from "@calcifer-design/tokens";
3
+ function minWidth(name) {
4
+ return `(min-width: ${breakpoint[name]})`;
5
+ }
6
+ function getServerSnapshot() {
7
+ return false;
8
+ }
9
+ function useMediaQuery(query) {
10
+ const subscribe = useCallback((onChange)=>{
11
+ const mediaQueryList = window.matchMedia(query);
12
+ mediaQueryList.addEventListener('change', onChange);
13
+ return ()=>mediaQueryList.removeEventListener('change', onChange);
14
+ }, [
15
+ query
16
+ ]);
17
+ const getSnapshot = useCallback(()=>window.matchMedia(query).matches, [
18
+ query
19
+ ]);
20
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
21
+ }
22
+ export { minWidth, useMediaQuery };