ppij-design-system 0.0.0-stage → 0.2.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 (151) hide show
  1. package/dist/banners.d.ts +31 -0
  2. package/dist/banners.js +18 -0
  3. package/dist/card-themes.d.ts +14 -0
  4. package/dist/card-themes.js +325 -0
  5. package/dist/components/account-menu.d.ts +24 -0
  6. package/dist/components/account-menu.js +13 -0
  7. package/dist/components/admin-drawer.d.ts +34 -0
  8. package/dist/components/admin-drawer.js +30 -0
  9. package/dist/components/alert-dialog.d.ts +16 -0
  10. package/dist/components/alert-dialog.js +39 -0
  11. package/dist/components/alert.d.ts +11 -0
  12. package/dist/components/alert.js +29 -0
  13. package/dist/components/avatar.d.ts +11 -0
  14. package/dist/components/avatar.js +22 -0
  15. package/dist/components/badge.d.ts +9 -0
  16. package/dist/components/badge.js +27 -0
  17. package/dist/components/banner-carousel.d.ts +14 -0
  18. package/dist/components/banner-carousel.js +90 -0
  19. package/dist/components/bottom-bar.d.ts +22 -0
  20. package/dist/components/bottom-bar.js +12 -0
  21. package/dist/components/button.d.ts +10 -0
  22. package/dist/components/button.js +37 -0
  23. package/dist/components/card-upload.d.ts +30 -0
  24. package/dist/components/card-upload.js +19 -0
  25. package/dist/components/card.d.ts +9 -0
  26. package/dist/components/card.js +24 -0
  27. package/dist/components/checkbox.d.ts +4 -0
  28. package/dist/components/checkbox.js +9 -0
  29. package/dist/components/data-table.d.ts +26 -0
  30. package/dist/components/data-table.js +17 -0
  31. package/dist/components/dialog.d.ts +17 -0
  32. package/dist/components/dialog.js +37 -0
  33. package/dist/components/drawer.d.ts +13 -0
  34. package/dist/components/drawer.js +34 -0
  35. package/dist/components/dropdown-menu.d.ts +25 -0
  36. package/dist/components/dropdown-menu.js +50 -0
  37. package/dist/components/editable-panel.d.ts +25 -0
  38. package/dist/components/editable-panel.js +18 -0
  39. package/dist/components/empty.d.ts +12 -0
  40. package/dist/components/empty.js +33 -0
  41. package/dist/components/file-upload.d.ts +28 -0
  42. package/dist/components/file-upload.js +21 -0
  43. package/dist/components/form-panel.d.ts +15 -0
  44. package/dist/components/form-panel.js +9 -0
  45. package/dist/components/funding-picker.d.ts +20 -0
  46. package/dist/components/funding-picker.js +16 -0
  47. package/dist/components/input.d.ts +3 -0
  48. package/dist/components/input.js +6 -0
  49. package/dist/components/invitation-card.d.ts +14 -0
  50. package/dist/components/invitation-card.js +13 -0
  51. package/dist/components/label.d.ts +4 -0
  52. package/dist/components/label.js +7 -0
  53. package/dist/components/member-card-block.d.ts +47 -0
  54. package/dist/components/member-card-block.js +329 -0
  55. package/dist/components/member-card.css +92 -0
  56. package/dist/components/member-card.d.ts +29 -0
  57. package/dist/components/member-card.js +22 -0
  58. package/dist/components/month-picker.d.ts +16 -0
  59. package/dist/components/month-picker.js +19 -0
  60. package/dist/components/offline-banner.d.ts +14 -0
  61. package/dist/components/offline-banner.js +14 -0
  62. package/dist/components/page-header.d.ts +14 -0
  63. package/dist/components/page-header.js +10 -0
  64. package/dist/components/phone-field.d.ts +25 -0
  65. package/dist/components/phone-field.js +64 -0
  66. package/dist/components/pill-radio.d.ts +21 -0
  67. package/dist/components/pill-radio.js +15 -0
  68. package/dist/components/portal-shell.d.ts +53 -0
  69. package/dist/components/portal-shell.js +19 -0
  70. package/dist/components/postcode-field.d.ts +26 -0
  71. package/dist/components/postcode-field.js +14 -0
  72. package/dist/components/progress.d.ts +4 -0
  73. package/dist/components/progress.js +8 -0
  74. package/dist/components/radio-group.d.ts +5 -0
  75. package/dist/components/radio-group.js +11 -0
  76. package/dist/components/review-panel.d.ts +20 -0
  77. package/dist/components/review-panel.js +14 -0
  78. package/dist/components/saving-indicator.d.ts +15 -0
  79. package/dist/components/saving-indicator.js +19 -0
  80. package/dist/components/scroll-area.d.ts +5 -0
  81. package/dist/components/scroll-area.js +13 -0
  82. package/dist/components/search-filter.d.ts +39 -0
  83. package/dist/components/search-filter.js +19 -0
  84. package/dist/components/segmented-progress.d.ts +16 -0
  85. package/dist/components/segmented-progress.js +12 -0
  86. package/dist/components/select.d.ts +15 -0
  87. package/dist/components/select.js +38 -0
  88. package/dist/components/separator.d.ts +4 -0
  89. package/dist/components/separator.js +7 -0
  90. package/dist/components/service-tiles.d.ts +26 -0
  91. package/dist/components/service-tiles.js +17 -0
  92. package/dist/components/sheet.d.ts +14 -0
  93. package/dist/components/sheet.js +40 -0
  94. package/dist/components/sidebar.d.ts +69 -0
  95. package/dist/components/sidebar.js +210 -0
  96. package/dist/components/skeleton.d.ts +11 -0
  97. package/dist/components/skeleton.js +11 -0
  98. package/dist/components/sonner.d.ts +9 -0
  99. package/dist/components/sonner.js +25 -0
  100. package/dist/components/spinner.d.ts +14 -0
  101. package/dist/components/spinner.js +22 -0
  102. package/dist/components/stat-card.d.ts +22 -0
  103. package/dist/components/stat-card.js +12 -0
  104. package/dist/components/status-card.d.ts +24 -0
  105. package/dist/components/status-card.js +26 -0
  106. package/dist/components/stepper.d.ts +25 -0
  107. package/dist/components/stepper.js +23 -0
  108. package/dist/components/submit-overlay.d.ts +16 -0
  109. package/dist/components/submit-overlay.js +15 -0
  110. package/dist/components/suggestion-sheet.d.ts +20 -0
  111. package/dist/components/suggestion-sheet.js +36 -0
  112. package/dist/components/switch.d.ts +6 -0
  113. package/dist/components/switch.js +8 -0
  114. package/dist/components/table.d.ts +10 -0
  115. package/dist/components/table.js +28 -0
  116. package/dist/components/tabs.d.ts +11 -0
  117. package/dist/components/tabs.js +28 -0
  118. package/dist/components/tag-input.d.ts +15 -0
  119. package/dist/components/tag-input.js +31 -0
  120. package/dist/components/textarea.d.ts +3 -0
  121. package/dist/components/textarea.js +6 -0
  122. package/dist/components/timeline.d.ts +18 -0
  123. package/dist/components/timeline.js +11 -0
  124. package/dist/components/todo-strip.d.ts +14 -0
  125. package/dist/components/todo-strip.js +11 -0
  126. package/dist/components/tooltip.d.ts +7 -0
  127. package/dist/components/tooltip.js +16 -0
  128. package/dist/components/top-bar.d.ts +23 -0
  129. package/dist/components/top-bar.js +14 -0
  130. package/dist/components/type-ahead.d.ts +20 -0
  131. package/dist/components/type-ahead.js +22 -0
  132. package/dist/components/unit-card.d.ts +26 -0
  133. package/dist/components/unit-card.js +18 -0
  134. package/dist/components/view-toggle.d.ts +20 -0
  135. package/dist/components/view-toggle.js +15 -0
  136. package/dist/components/wizard-footer.d.ts +24 -0
  137. package/dist/components/wizard-footer.js +17 -0
  138. package/dist/components/wizard-header.d.ts +29 -0
  139. package/dist/components/wizard-header.js +18 -0
  140. package/dist/hooks/use-mobile.d.ts +1 -0
  141. package/dist/hooks/use-mobile.js +11 -0
  142. package/dist/index.d.ts +70 -0
  143. package/dist/index.js +70 -0
  144. package/dist/lib/utils.d.ts +2 -0
  145. package/dist/lib/utils.js +5 -0
  146. package/dist/sample-banners.d.ts +8 -0
  147. package/dist/sample-banners.js +28 -0
  148. package/dist/tokens.css +119 -0
  149. package/dist/tokens.json +50 -0
  150. package/package.json +60 -4
  151. package/README.md +0 -3
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils";
3
+ /**
4
+ * Top navigation bar pattern for the public site and the portal.
5
+ * Brand left, section links in the middle, quiet actions right.
6
+ * The language switch stays a quiet text action, never boxed codes.
7
+ * On narrow screens the links hide and the caller provides a menu
8
+ * through the actions slot.
9
+ */
10
+ export function TopBar({ brand, brandHref = "/", links = [], actions, className }) {
11
+ return (_jsx("header", { "data-slot": "top-bar", className: cn("w-full border-b border-brand-border bg-white", className), children: _jsxs("div", { className: "mx-auto flex h-14 max-w-6xl items-center gap-4 px-4 sm:px-6", children: [_jsx("a", { href: brandHref, className: "flex shrink-0 items-center gap-2 text-brand-ink no-underline", children: brand }), links.length > 0 ? (_jsx("nav", { "aria-label": "Utama", className: "ml-2 hidden items-center gap-1 md:flex", children: links.map((link) => (_jsx("a", { href: link.href, "aria-current": link.current ? "page" : undefined, className: cn("rounded-md px-3 py-2 text-sm no-underline", link.current
12
+ ? "font-semibold text-brand-red"
13
+ : "text-brand-ink hover:bg-brand-bg hover:text-brand-red"), children: link.label }, link.label + link.href))) })) : null, actions ? _jsx("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: actions }) : null] }) }));
14
+ }
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ export interface TypeAheadProps {
3
+ /** Current text in the field; the chosen option once picked. */
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ /** Called when the person picks a suggestion. */
7
+ onSelect?: (value: string) => void;
8
+ /** Suggestion source; filtered by the current text inside the component. */
9
+ options: string[];
10
+ placeholder?: string;
11
+ id?: string;
12
+ className?: string;
13
+ }
14
+ /**
15
+ * A text field with suggestions: the person types, matching options
16
+ * appear under the field, and picking one fills the field. Free text
17
+ * stays allowed; the page decides whether the value must come from the
18
+ * list. Used for institutions, majors, and cities in the wizard.
19
+ */
20
+ export declare function TypeAhead({ value, onChange, onSelect, options, placeholder, id, className }: TypeAheadProps): React.JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { IconChevronDown } from "@tabler/icons-react";
4
+ import { cn } from "../lib/utils";
5
+ import { Input } from "./input";
6
+ /**
7
+ * A text field with suggestions: the person types, matching options
8
+ * appear under the field, and picking one fills the field. Free text
9
+ * stays allowed; the page decides whether the value must come from the
10
+ * list. Used for institutions, majors, and cities in the wizard.
11
+ */
12
+ export function TypeAhead({ value, onChange, onSelect, options, placeholder, id, className }) {
13
+ const [open, setOpen] = React.useState(false);
14
+ const matches = options.filter((option) => option.toLowerCase().includes(value.toLowerCase()) && option !== value).slice(0, 8);
15
+ const showList = open && value.trim() !== "" && matches.length > 0;
16
+ return (_jsxs("div", { className: cn("relative", className), children: [_jsx(Input, { id: id, value: value, placeholder: placeholder, autoComplete: "off", onChange: (event) => { onChange(event.target.value); setOpen(true); }, onFocus: () => setOpen(true), onBlur: () => setTimeout(() => setOpen(false), 120), className: "pr-9" }), _jsx(IconChevronDown, { className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground" }), showList && (_jsx("ul", { className: "absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-md border bg-popover p-1 shadow-md", role: "listbox", "aria-label": "Saran", children: matches.map((option) => (_jsx("li", { children: _jsx("button", { type: "button", role: "option", "aria-selected": "false", className: "w-full rounded-sm px-2 py-1.5 text-left text-sm outline-none hover:bg-accent focus-visible:bg-accent", onMouseDown: (event) => {
17
+ event.preventDefault();
18
+ onChange(option);
19
+ onSelect?.(option);
20
+ setOpen(false);
21
+ }, children: option }) }, option))) }))] }));
22
+ }
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ export interface UnitCardProps {
3
+ /** Unit name, for example "Okayama". */
4
+ name: string;
5
+ /** Unit kind shown as a quiet badge, for example "Komsat" or "Wilayah". */
6
+ kind: string;
7
+ /** Number of members in the unit. */
8
+ memberCount: number;
9
+ /** Name of the person who leads the unit. */
10
+ leadName?: string;
11
+ /** Status label, for example "Aktif". */
12
+ statusLabel: string;
13
+ /** Status badge tone. */
14
+ statusVariant?: "default" | "success" | "warning" | "secondary";
15
+ /** Where the card leads. When set, the whole card is a link. */
16
+ href?: string;
17
+ /** Extra actions rendered at the card foot, for example an Edit button. */
18
+ actions?: React.ReactNode;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * One organisational unit (komsat, wilayah) as a card: identity first,
23
+ * member count and lead as quiet facts, status as a badge beside the
24
+ * name. Presentation only; membership and permissions stay in the app.
25
+ */
26
+ export declare function UnitCard({ name, kind, memberCount, leadName, statusLabel, statusVariant, href, actions, className, }: UnitCardProps): React.JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { IconUsers, IconChevronRight } from "@tabler/icons-react";
3
+ import { cn } from "../lib/utils";
4
+ import { Card } from "./card";
5
+ import { Badge } from "./badge";
6
+ /**
7
+ * One organisational unit (komsat, wilayah) as a card: identity first,
8
+ * member count and lead as quiet facts, status as a badge beside the
9
+ * name. Presentation only; membership and permissions stay in the app.
10
+ */
11
+ export function UnitCard({ name, kind, memberCount, leadName, statusLabel, statusVariant = "success", href, actions, className, }) {
12
+ const body = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "grid gap-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "text-base font-semibold text-brand-ink", children: name }), _jsx(Badge, { variant: "secondary", children: kind })] }), _jsx("span", { className: "text-sm text-muted-foreground", children: leadName ? "Ketua: " + leadName : "Belum ada ketua" })] }), _jsx(Badge, { variant: statusVariant, children: statusLabel })] }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("span", { className: "inline-flex items-center gap-1.5 text-sm text-muted-foreground", children: [_jsx(IconUsers, { className: "size-4" }), memberCount, " anggota"] }), href ? (_jsx(IconChevronRight, { className: "size-4 text-muted-foreground" })) : (actions)] })] }));
13
+ const classes = cn("grid gap-4 p-5", className);
14
+ if (href) {
15
+ return (_jsx(Card, { className: cn(classes, "transition-colors hover:border-brand-red/40"), children: _jsx("a", { href: href, className: "grid gap-4 no-underline outline-none focus-visible:ring-2 focus-visible:ring-ring", children: body }) }));
16
+ }
17
+ return _jsx(Card, { className: classes, children: body });
18
+ }
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ export interface ViewToggleOption {
3
+ label: string;
4
+ href: string;
5
+ current?: boolean;
6
+ }
7
+ export interface ViewToggleProps {
8
+ options: ViewToggleOption[];
9
+ ariaLabel: string;
10
+ className?: string;
11
+ }
12
+ /**
13
+ * Segmented toggle, generalized from the portal ViewSwitch: a quiet
14
+ * grey track holds the segments; the current side is a white segment
15
+ * with ink text and a small shadow, the other side is muted text.
16
+ * Red is reserved for primary actions and focus, never for navigation
17
+ * state. Navigation arrives as props (labels and hrefs); role and
18
+ * visibility rules stay in the application.
19
+ */
20
+ export declare function ViewToggle({ options, ariaLabel, className }: ViewToggleProps): React.JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils";
3
+ /**
4
+ * Segmented toggle, generalized from the portal ViewSwitch: a quiet
5
+ * grey track holds the segments; the current side is a white segment
6
+ * with ink text and a small shadow, the other side is muted text.
7
+ * Red is reserved for primary actions and focus, never for navigation
8
+ * state. Navigation arrives as props (labels and hrefs); role and
9
+ * visibility rules stay in the application.
10
+ */
11
+ export function ViewToggle({ options, ariaLabel, className }) {
12
+ return (_jsx("div", { className: className, children: _jsx("div", { role: "group", "aria-label": ariaLabel, className: "inline-flex items-center gap-1 rounded-full bg-secondary p-1", children: options.map((option) => (_jsx("a", { href: option.href, "aria-current": option.current ? "page" : undefined, className: cn("flex h-10 min-w-24 items-center justify-center rounded-full px-4 text-sm no-underline focus-visible:outline-2 focus-visible:outline-brand-red", option.current
13
+ ? "bg-card font-semibold text-brand-ink shadow-sm"
14
+ : "text-muted-foreground hover:text-brand-ink"), children: option.label }, option.label + option.href))) }) }));
15
+ }
@@ -0,0 +1,24 @@
1
+ export interface WizardFooterProps {
2
+ onBack: () => void;
3
+ onContinue: () => void;
4
+ backLabel?: string;
5
+ continueLabel?: string;
6
+ backDisabled?: boolean;
7
+ continueDisabled?: boolean;
8
+ /** While continuing submits: spinner plus loadingLabel inside the primary button. */
9
+ continueLoading?: boolean;
10
+ loadingLabel?: string;
11
+ /** The final submit carries no arrow. */
12
+ showArrow?: boolean;
13
+ className?: string;
14
+ }
15
+ /**
16
+ * The wizard's fixed bottom navigation bar, extracted from
17
+ * Wizard.tsx: two equal buttons (Kembali outline, Lanjut primary)
18
+ * in a white bar pinned to the viewport bottom. Named exception to
19
+ * the default button size: a fixed navigation bar is a navigation
20
+ * target, so its buttons are field height (44px) and text-base,
21
+ * like the portal bottom nav. The loading state follows the rule:
22
+ * spinner inside the primary button.
23
+ */
24
+ export declare function WizardFooter({ onBack, onContinue, backLabel, continueLabel, backDisabled, continueDisabled, continueLoading, loadingLabel, showArrow, className, }: WizardFooterProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react";
3
+ import { cn } from "../lib/utils";
4
+ import { Button } from "./button";
5
+ import { Spinner } from "./spinner";
6
+ /**
7
+ * The wizard's fixed bottom navigation bar, extracted from
8
+ * Wizard.tsx: two equal buttons (Kembali outline, Lanjut primary)
9
+ * in a white bar pinned to the viewport bottom. Named exception to
10
+ * the default button size: a fixed navigation bar is a navigation
11
+ * target, so its buttons are field height (44px) and text-base,
12
+ * like the portal bottom nav. The loading state follows the rule:
13
+ * spinner inside the primary button.
14
+ */
15
+ export function WizardFooter({ onBack, onContinue, backLabel = "Kembali", continueLabel = "Lanjut", backDisabled = false, continueDisabled = false, continueLoading = false, loadingLabel = "Mengirim…", showArrow = true, className, }) {
16
+ return (_jsx("div", { className: cn("fixed inset-x-0 bottom-0 z-30 border-t border-border bg-card", className), children: _jsxs("div", { className: "mx-auto flex max-w-2xl gap-3 px-4 py-3", children: [_jsxs(Button, { type: "button", variant: "outline", onClick: onBack, disabled: backDisabled, className: "min-h-11 flex-1 text-base font-semibold", children: [_jsx(IconArrowLeft, { size: 18, "aria-hidden": "true" }), backLabel] }), _jsx(Button, { type: "submit", onClick: onContinue, disabled: continueDisabled || continueLoading, "aria-busy": continueLoading, className: "min-h-11 flex-1 text-base font-semibold", children: continueLoading ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { size: "sm", decorative: true }), _jsx("span", { children: loadingLabel })] })) : (_jsxs(_Fragment, { children: [continueLabel, showArrow && _jsx(IconArrowRight, { size: 18, "aria-hidden": "true" })] })) })] }) }));
17
+ }
@@ -0,0 +1,29 @@
1
+ import { type SavingStatus } from "./saving-indicator";
2
+ export interface WizardLanguage {
3
+ label: string;
4
+ current?: boolean;
5
+ }
6
+ export interface WizardHeaderProps {
7
+ /** Counter text, for example "Langkah 3 dari 7". */
8
+ stepText: string;
9
+ /** Step title shown after the counter on sm and wider. */
10
+ title: string;
11
+ current: number;
12
+ total: number;
13
+ progressLabel: string;
14
+ savingStatus: SavingStatus;
15
+ locale?: "id" | "en";
16
+ languages: WizardLanguage[];
17
+ languageLabel?: string;
18
+ onLanguageSelect?: (language: WizardLanguage) => void;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * The wizard's sticky sub-header (WIZ106-09), extracted from
23
+ * Wizard.tsx: step counter with the step title, the draft
24
+ * SavingIndicator and the language menu on the right, and the
25
+ * segmented progress below. It sits under the portal header inside
26
+ * the shell content; the consumer supplies the horizontal bleed
27
+ * (for example -mx-4) its layout needs.
28
+ */
29
+ export declare function WizardHeader({ stepText, title, current, total, progressLabel, savingStatus, locale, languages, languageLabel, onLanguageSelect, className, }: WizardHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { IconCheck, IconLanguage } from "@tabler/icons-react";
3
+ import { cn } from "../lib/utils";
4
+ import { Button } from "./button";
5
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "./dropdown-menu";
6
+ import { SavingIndicator } from "./saving-indicator";
7
+ import { SegmentedProgress } from "./segmented-progress";
8
+ /**
9
+ * The wizard's sticky sub-header (WIZ106-09), extracted from
10
+ * Wizard.tsx: step counter with the step title, the draft
11
+ * SavingIndicator and the language menu on the right, and the
12
+ * segmented progress below. It sits under the portal header inside
13
+ * the shell content; the consumer supplies the horizontal bleed
14
+ * (for example -mx-4) its layout needs.
15
+ */
16
+ export function WizardHeader({ stepText, title, current, total, progressLabel, savingStatus, locale = "id", languages, languageLabel = "Pilih bahasa", onLanguageSelect, className, }) {
17
+ return (_jsxs("div", { className: cn("sticky top-0 z-40 border-b border-border bg-card px-4 py-3", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("p", { className: "min-w-0 text-sm leading-5 font-medium text-brand-ink", "aria-live": "polite", children: [stepText, _jsxs("span", { className: "hidden sm:inline", children: [" \u2014 ", title] })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsx(SavingIndicator, { status: savingStatus, locale: locale }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", "aria-label": languageLabel, children: _jsx(IconLanguage, {}) }) }), _jsx(DropdownMenuContent, { align: "end", children: languages.map((language) => (_jsxs(DropdownMenuItem, { onSelect: () => onLanguageSelect?.(language), children: [_jsx("span", { className: "flex-1", children: language.label }), language.current && _jsx(IconCheck, { className: "size-4 text-brand-red", "aria-hidden": "true" })] }, language.label))) })] })] })] }), _jsx(SegmentedProgress, { current: current, total: total, label: progressLabel, className: "mt-3" })] }));
18
+ }
@@ -0,0 +1 @@
1
+ export declare function useIsMobile(): boolean;
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ const MOBILE_BREAKPOINT = 768;
3
+ const MOBILE_QUERY = `(max-width: ${MOBILE_BREAKPOINT - 1}px)`;
4
+ function subscribeMobile(onChange) {
5
+ const mql = window.matchMedia(MOBILE_QUERY);
6
+ mql.addEventListener("change", onChange);
7
+ return () => mql.removeEventListener("change", onChange);
8
+ }
9
+ export function useIsMobile() {
10
+ return React.useSyncExternalStore(subscribeMobile, () => window.matchMedia(MOBILE_QUERY).matches, () => false);
11
+ }
@@ -0,0 +1,70 @@
1
+ export { cn } from "./lib/utils";
2
+ export { Button, buttonVariants } from "./components/button";
3
+ export { Badge, badgeVariants } from "./components/badge";
4
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from "./components/card";
5
+ export { Input } from "./components/input";
6
+ export { Label } from "./components/label";
7
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./components/select";
8
+ export { Separator } from "./components/separator";
9
+ export { Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup, AvatarGroupCount, } from "./components/avatar";
10
+ export { Spinner } from "./components/spinner";
11
+ export { Skeleton } from "./components/skeleton";
12
+ export { Alert, AlertTitle, AlertDescription } from "./components/alert";
13
+ export { SavingIndicator, type SavingStatus, type SavingIndicatorProps } from "./components/saving-indicator";
14
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./components/tooltip";
15
+ export { ScrollArea, ScrollBar } from "./components/scroll-area";
16
+ export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, } from "./components/drawer";
17
+ export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, } from "./components/sheet";
18
+ export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "./components/dropdown-menu";
19
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } from "./components/tabs";
20
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, } from "./components/table";
21
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, } from "./components/sidebar";
22
+ export { AdminDrawer, useIsDesktop, type AdminDrawerProps, type Locale } from "./components/admin-drawer";
23
+ export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia } from "./components/empty";
24
+ export { FormPanel } from "./components/form-panel";
25
+ export { MemberCard, MemberCardBack, type MemberCardProps, type MemberCardBackProps, type MemberCardTheme } from "./components/member-card";
26
+ export { PageHeader, type PageHeaderProps } from "./components/page-header";
27
+ export { DataTable, type DataTableProps } from "./components/data-table";
28
+ export { TopBar, type TopBarProps, type TopBarLink } from "./components/top-bar";
29
+ export { ViewToggle, type ViewToggleProps, type ViewToggleOption } from "./components/view-toggle";
30
+ export { BottomBar, type BottomBarProps, type BottomBarItem } from "./components/bottom-bar";
31
+ export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/dialog";
32
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, } from "./components/alert-dialog";
33
+ export { Toaster } from "./components/sonner";
34
+ export { SearchFilter, type SearchFilterProps, type ActiveFilter } from "./components/search-filter";
35
+ export { UnitCard, type UnitCardProps } from "./components/unit-card";
36
+ export { StatCard, type StatCardProps } from "./components/stat-card";
37
+ export { Checkbox } from "./components/checkbox";
38
+ export { RadioGroup, RadioGroupItem } from "./components/radio-group";
39
+ export { Switch } from "./components/switch";
40
+ export { Textarea } from "./components/textarea";
41
+ export { Stepper, type StepperProps, type StepperStep } from "./components/stepper";
42
+ export { FileUpload, type FileUploadProps, type FileUploadStatus } from "./components/file-upload";
43
+ export { Progress } from "./components/progress";
44
+ export { Timeline, type TimelineProps, type TimelineItem } from "./components/timeline";
45
+ export { AccountMenu, type AccountMenuProps, type AccountMenuItem } from "./components/account-menu";
46
+ export { TypeAhead, type TypeAheadProps } from "./components/type-ahead";
47
+ export { TagInput, type TagInputProps } from "./components/tag-input";
48
+ export { PortalShell, type PortalShellProps, type PortalNavItem, type PortalLanguage } from "./components/portal-shell";
49
+ export { StatusCard, type StatusCardProps, type StatusCardTone } from "./components/status-card";
50
+ export { ServiceTiles, type ServiceTilesProps, type ServiceTile, type ServiceTileGroup } from "./components/service-tiles";
51
+ export { MemberCardBlock, hasBarcode, type MemberCardBlockProps, type MemberCardState, type CardThemePreference } from "./components/member-card-block";
52
+ export { PhoneField, PHONE_COUNTRY_CODES, type PhoneFieldProps } from "./components/phone-field";
53
+ export { SegmentedProgress, type SegmentedProgressProps } from "./components/segmented-progress";
54
+ export { WizardHeader, type WizardHeaderProps, type WizardLanguage } from "./components/wizard-header";
55
+ export { PillRadio, pillRadioClass, type PillRadioProps, type PillRadioOption } from "./components/pill-radio";
56
+ export { MonthPicker, type MonthPickerProps } from "./components/month-picker";
57
+ export { PostcodeField, type PostcodeFieldProps, type PostcodeCandidate } from "./components/postcode-field";
58
+ export { FundingPicker, type FundingPickerProps } from "./components/funding-picker";
59
+ export { CardUpload, type CardUploadProps, type CardSide, type CardSideState } from "./components/card-upload";
60
+ export { SuggestionSheet, type SuggestionSheetProps, type SuggestKind } from "./components/suggestion-sheet";
61
+ export { SubmitOverlay, type SubmitOverlayProps } from "./components/submit-overlay";
62
+ export { ReviewPanel, type ReviewPanelProps } from "./components/review-panel";
63
+ export { WizardFooter, type WizardFooterProps } from "./components/wizard-footer";
64
+ export { BannerCarousel, type BannerCarouselProps } from "./components/banner-carousel";
65
+ export { OfflineBanner, type OfflineBannerProps } from "./components/offline-banner";
66
+ export { TodoStrip, type TodoStripProps } from "./components/todo-strip";
67
+ export { InvitationCard, type InvitationCardProps } from "./components/invitation-card";
68
+ export { cardThemes, resolveCardTheme, type CardTheme } from "./card-themes";
69
+ export { activeBanners, jstToday, MAX_ACTIVE, type PromoBanner } from "./banners";
70
+ export { EditablePanel, type EditablePanelProps } from "./components/editable-panel";
package/dist/index.js ADDED
@@ -0,0 +1,70 @@
1
+ export { cn } from "./lib/utils";
2
+ export { Button, buttonVariants } from "./components/button";
3
+ export { Badge, badgeVariants } from "./components/badge";
4
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from "./components/card";
5
+ export { Input } from "./components/input";
6
+ export { Label } from "./components/label";
7
+ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./components/select";
8
+ export { Separator } from "./components/separator";
9
+ export { Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup, AvatarGroupCount, } from "./components/avatar";
10
+ export { Spinner } from "./components/spinner";
11
+ export { Skeleton } from "./components/skeleton";
12
+ export { Alert, AlertTitle, AlertDescription } from "./components/alert";
13
+ export { SavingIndicator } from "./components/saving-indicator";
14
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./components/tooltip";
15
+ export { ScrollArea, ScrollBar } from "./components/scroll-area";
16
+ export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, } from "./components/drawer";
17
+ export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, } from "./components/sheet";
18
+ export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "./components/dropdown-menu";
19
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } from "./components/tabs";
20
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, } from "./components/table";
21
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, } from "./components/sidebar";
22
+ export { AdminDrawer, useIsDesktop } from "./components/admin-drawer";
23
+ export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia } from "./components/empty";
24
+ export { FormPanel } from "./components/form-panel";
25
+ export { MemberCard, MemberCardBack } from "./components/member-card";
26
+ export { PageHeader } from "./components/page-header";
27
+ export { DataTable } from "./components/data-table";
28
+ export { TopBar } from "./components/top-bar";
29
+ export { ViewToggle } from "./components/view-toggle";
30
+ export { BottomBar } from "./components/bottom-bar";
31
+ export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/dialog";
32
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, } from "./components/alert-dialog";
33
+ export { Toaster } from "./components/sonner";
34
+ export { SearchFilter } from "./components/search-filter";
35
+ export { UnitCard } from "./components/unit-card";
36
+ export { StatCard } from "./components/stat-card";
37
+ export { Checkbox } from "./components/checkbox";
38
+ export { RadioGroup, RadioGroupItem } from "./components/radio-group";
39
+ export { Switch } from "./components/switch";
40
+ export { Textarea } from "./components/textarea";
41
+ export { Stepper } from "./components/stepper";
42
+ export { FileUpload } from "./components/file-upload";
43
+ export { Progress } from "./components/progress";
44
+ export { Timeline } from "./components/timeline";
45
+ export { AccountMenu } from "./components/account-menu";
46
+ export { TypeAhead } from "./components/type-ahead";
47
+ export { TagInput } from "./components/tag-input";
48
+ export { PortalShell } from "./components/portal-shell";
49
+ export { StatusCard } from "./components/status-card";
50
+ export { ServiceTiles } from "./components/service-tiles";
51
+ export { MemberCardBlock, hasBarcode } from "./components/member-card-block";
52
+ export { PhoneField, PHONE_COUNTRY_CODES } from "./components/phone-field";
53
+ export { SegmentedProgress } from "./components/segmented-progress";
54
+ export { WizardHeader } from "./components/wizard-header";
55
+ export { PillRadio, pillRadioClass } from "./components/pill-radio";
56
+ export { MonthPicker } from "./components/month-picker";
57
+ export { PostcodeField } from "./components/postcode-field";
58
+ export { FundingPicker } from "./components/funding-picker";
59
+ export { CardUpload } from "./components/card-upload";
60
+ export { SuggestionSheet } from "./components/suggestion-sheet";
61
+ export { SubmitOverlay } from "./components/submit-overlay";
62
+ export { ReviewPanel } from "./components/review-panel";
63
+ export { WizardFooter } from "./components/wizard-footer";
64
+ export { BannerCarousel } from "./components/banner-carousel";
65
+ export { OfflineBanner } from "./components/offline-banner";
66
+ export { TodoStrip } from "./components/todo-strip";
67
+ export { InvitationCard } from "./components/invitation-card";
68
+ export { cardThemes, resolveCardTheme } from "./card-themes";
69
+ export { activeBanners, jstToday, MAX_ACTIVE } from "./banners";
70
+ export { EditablePanel } from "./components/editable-panel";
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,5 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1,8 @@
1
+ import type { PromoBanner } from "./banners";
2
+ /**
3
+ * The real banners.json entries from ppij-identity-sso (October
4
+ * 2026) with the real artwork from brand/banners, for stories and
5
+ * prototypes. Dates are JST; expired entries drop out by
6
+ * themselves in BannerCarousel.
7
+ */
8
+ export declare const SAMPLE_BANNERS: PromoBanner[];
@@ -0,0 +1,28 @@
1
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
2
+ import b1d from "../../../brand/banners/banner_1.jpg";
3
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
4
+ import b1m from "../../../brand/banners/banner_1_mobile.jpg";
5
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
6
+ import b2d from "../../../brand/banners/banner_2.jpg";
7
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
8
+ import b2m from "../../../brand/banners/banner_2_mobile.jpg";
9
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
10
+ import b3d from "../../../brand/banners/banner_3.jpg";
11
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
12
+ import b3m from "../../../brand/banners/banner_3_mobile.jpg";
13
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
14
+ import b4d from "../../../brand/banners/banner_4.jpg";
15
+ // @ts-expect-error Static banner artwork; the portal serves its own copies from /banners.
16
+ import b4m from "../../../brand/banners/banner_4_mobile.jpg";
17
+ /**
18
+ * The real banners.json entries from ppij-identity-sso (October
19
+ * 2026) with the real artwork from brand/banners, for stories and
20
+ * prototypes. Dates are JST; expired entries drop out by
21
+ * themselves in BannerCarousel.
22
+ */
23
+ export const SAMPLE_BANNERS = [
24
+ { id: "banner_4", image: { desktop: b4d, mobile: b4m }, alt: { id: "Webinar gratis Communication Survival Skills 2027 bersama John Robert Powers, Sabtu 31 Oktober 2026 pukul 14.00 WIB via Zoom. Daftar sekarang.", en: "Free Communication Survival Skills 2027 webinar with John Robert Powers, Saturday 31 October 2026, 14.00 WIB on Zoom. Register now." }, href: "https://bit.ly/CSSSeminar2026", start: "2026-10-24", end: "2026-11-01", order: 1 },
25
+ { id: "banner_1", image: { desktop: b1d, mobile: b1m }, alt: { id: "Diskon 10% di Toko Indonesia Tokyo untuk anggota PPI Jepang", en: "10% off at Toko Indonesia Tokyo for PPI Japan members" }, href: "https://ppijepang.org", start: "2026-10-20", end: "2026-12-31", order: 2 },
26
+ { id: "banner_2", image: { desktop: b2d, mobile: b2m }, alt: { id: "Gratis ongkir kirim paket ke Indonesia untuk anggota aktif", en: "Free shipping to Indonesia for active members" }, href: "https://ppijepang.org", start: "2026-10-20", end: "2026-12-31", order: 3 },
27
+ { id: "banner_3", image: { desktop: b3d, mobile: b3m }, alt: { id: "Ski & Snowboard bareng PPI Okayama musim dingin ini. Daftar sekarang.", en: "Join the PPI Okayama Winter Ski and Snowboard trip. Register now." }, href: "https://ppijepang.org", start: "2026-10-21", order: 4 },
28
+ ];
@@ -0,0 +1,119 @@
1
+ @import "tailwindcss";
2
+
3
+ /*
4
+ * GENERATED from tokens/tokens.json by scripts/generate-tokens.mjs.
5
+ * Do not edit by hand. PPIJ design tokens: brand primitives in :root,
6
+ * the shadcn semantic layer mapped to them, Tailwind v4 theme names.
7
+ */
8
+ :root {
9
+ --brand-red: #c8102e;
10
+ --brand-ink: #1c1f23;
11
+ --brand-muted: #5b6168;
12
+ --brand-border: #e3e5e8;
13
+ --brand-bg: #f6f6f4;
14
+ --status-green: #15803d;
15
+ --status-amber: #b45309;
16
+ --status-blue: #1d4ed8;
17
+
18
+ --background: var(--brand-bg);
19
+ --foreground: var(--brand-ink);
20
+ --card: #ffffff;
21
+ --card-foreground: var(--brand-ink);
22
+ --popover: #ffffff;
23
+ --popover-foreground: var(--brand-ink);
24
+ --primary: var(--brand-red);
25
+ --primary-foreground: #ffffff;
26
+ --secondary: #eceef0;
27
+ --secondary-foreground: var(--brand-ink);
28
+ --muted: #eceef0;
29
+ --muted-foreground: var(--brand-muted);
30
+ --accent: #eceef0;
31
+ --accent-foreground: var(--brand-ink);
32
+ --destructive: var(--brand-red);
33
+ --border: var(--brand-border);
34
+ --input: var(--brand-border);
35
+ --ring: var(--brand-red);
36
+ --sidebar: #ffffff;
37
+ --sidebar-foreground: var(--brand-ink);
38
+ --sidebar-primary: var(--brand-red);
39
+ --sidebar-primary-foreground: #ffffff;
40
+ --sidebar-accent: #eceef0;
41
+ --sidebar-accent-foreground: var(--brand-ink);
42
+ --sidebar-border: var(--brand-border);
43
+ --sidebar-ring: var(--brand-red);
44
+ --success: var(--status-green);
45
+ --success-foreground: #ffffff;
46
+ --warning: var(--status-amber);
47
+ --warning-foreground: #ffffff;
48
+ --info: var(--status-blue);
49
+ --info-foreground: #ffffff;
50
+ --radius: 0.625rem;
51
+ }
52
+
53
+ @theme inline {
54
+ --font-sans: "Plus Jakarta Sans", "Noto Sans JP", system-ui, sans-serif;
55
+
56
+ --color-brand-red: var(--brand-red);
57
+ --color-brand-ink: var(--brand-ink);
58
+ --color-brand-muted: var(--brand-muted);
59
+ --color-brand-border: var(--brand-border);
60
+ --color-brand-bg: var(--brand-bg);
61
+ --color-status-green: var(--status-green);
62
+ --color-status-amber: var(--status-amber);
63
+ --color-status-blue: var(--status-blue);
64
+ --color-background: var(--background);
65
+ --color-foreground: var(--foreground);
66
+ --color-card: var(--card);
67
+ --color-card-foreground: var(--card-foreground);
68
+ --color-popover: var(--popover);
69
+ --color-popover-foreground: var(--popover-foreground);
70
+ --color-primary: var(--primary);
71
+ --color-primary-foreground: var(--primary-foreground);
72
+ --color-secondary: var(--secondary);
73
+ --color-secondary-foreground: var(--secondary-foreground);
74
+ --color-muted: var(--muted);
75
+ --color-muted-foreground: var(--muted-foreground);
76
+ --color-accent: var(--accent);
77
+ --color-accent-foreground: var(--accent-foreground);
78
+ --color-destructive: var(--destructive);
79
+ --color-border: var(--border);
80
+ --color-input: var(--input);
81
+ --color-ring: var(--ring);
82
+ --color-sidebar: var(--sidebar);
83
+ --color-sidebar-foreground: var(--sidebar-foreground);
84
+ --color-sidebar-primary: var(--sidebar-primary);
85
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
86
+ --color-sidebar-accent: var(--sidebar-accent);
87
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
88
+ --color-sidebar-border: var(--sidebar-border);
89
+ --color-sidebar-ring: var(--sidebar-ring);
90
+ --color-success: var(--success);
91
+ --color-success-foreground: var(--success-foreground);
92
+ --color-warning: var(--warning);
93
+ --color-warning-foreground: var(--warning-foreground);
94
+ --color-info: var(--info);
95
+ --color-info-foreground: var(--info-foreground);
96
+
97
+ --radius-sm: calc(var(--radius) - 4px);
98
+ --radius-md: calc(var(--radius) - 2px);
99
+ --radius-lg: var(--radius);
100
+ --radius-xl: calc(var(--radius) + 4px);
101
+ }
102
+
103
+ @layer base {
104
+ * {
105
+ border-color: var(--border);
106
+ }
107
+
108
+ html {
109
+ -webkit-text-size-adjust: 100%;
110
+ }
111
+
112
+ body {
113
+ margin: 0;
114
+ background-color: var(--brand-bg);
115
+ color: var(--brand-ink);
116
+ font-family: var(--font-sans);
117
+ line-height: 1.6;
118
+ }
119
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "primitive": {
3
+ "brand-red": "#c8102e",
4
+ "brand-ink": "#1c1f23",
5
+ "brand-muted": "#5b6168",
6
+ "brand-border": "#e3e5e8",
7
+ "brand-bg": "#f6f6f4",
8
+ "white": "#ffffff",
9
+ "secondary-surface": "#eceef0",
10
+ "radius": "0.625rem",
11
+ "font-sans": "\"Plus Jakarta Sans\", \"Noto Sans JP\", system-ui, sans-serif",
12
+ "status-green": "#15803d",
13
+ "status-amber": "#b45309",
14
+ "status-blue": "#1d4ed8"
15
+ },
16
+ "semantic": {
17
+ "background": "var(--brand-bg)",
18
+ "foreground": "var(--brand-ink)",
19
+ "card": "#ffffff",
20
+ "card-foreground": "var(--brand-ink)",
21
+ "popover": "#ffffff",
22
+ "popover-foreground": "var(--brand-ink)",
23
+ "primary": "var(--brand-red)",
24
+ "primary-foreground": "#ffffff",
25
+ "secondary": "#eceef0",
26
+ "secondary-foreground": "var(--brand-ink)",
27
+ "muted": "#eceef0",
28
+ "muted-foreground": "var(--brand-muted)",
29
+ "accent": "#eceef0",
30
+ "accent-foreground": "var(--brand-ink)",
31
+ "destructive": "var(--brand-red)",
32
+ "border": "var(--brand-border)",
33
+ "input": "var(--brand-border)",
34
+ "ring": "var(--brand-red)",
35
+ "sidebar": "#ffffff",
36
+ "sidebar-foreground": "var(--brand-ink)",
37
+ "sidebar-primary": "var(--brand-red)",
38
+ "sidebar-primary-foreground": "#ffffff",
39
+ "sidebar-accent": "#eceef0",
40
+ "sidebar-accent-foreground": "var(--brand-ink)",
41
+ "sidebar-border": "var(--brand-border)",
42
+ "sidebar-ring": "var(--brand-red)",
43
+ "success": "var(--status-green)",
44
+ "success-foreground": "#ffffff",
45
+ "warning": "var(--status-amber)",
46
+ "warning-foreground": "#ffffff",
47
+ "info": "var(--status-blue)",
48
+ "info-foreground": "#ffffff"
49
+ }
50
+ }