@indinno/digicode-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.
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export interface EmptyStateProps {
4
+ icon?: ReactNode;
5
+ title: ReactNode;
6
+ description?: ReactNode;
7
+ actions?: ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ export function EmptyState({ icon, title, description, actions, className }: EmptyStateProps) {
12
+ return (
13
+ <div className={className ? `digicode-empty ${className}` : "digicode-empty"}>
14
+ {icon && <div className="digicode-empty__icon">{icon}</div>}
15
+ <h3 className="digicode-empty__title">{title}</h3>
16
+ {description && <p className="digicode-empty__description">{description}</p>}
17
+ {actions && <div className="digicode-empty__actions">{actions}</div>}
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,28 @@
1
+ import { forwardRef, type InputHTMLAttributes, type ReactNode } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
5
+ label?: string;
6
+ hint?: string;
7
+ error?: string;
8
+ prefix?: ReactNode;
9
+ suffix?: ReactNode;
10
+ }
11
+
12
+ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
13
+ { className, id, label, hint, error, prefix, suffix, ...props },
14
+ ref
15
+ ) {
16
+ const inputId = id || props.name;
17
+ return (
18
+ <label className="digicode-field" htmlFor={inputId}>
19
+ {label && <span className="digicode-field__label">{label}</span>}
20
+ <span className={cn("digicode-input-wrap", error && "digicode-input-wrap--error")}>
21
+ {prefix && <span className="digicode-input__affix">{prefix}</span>}
22
+ <input ref={ref} id={inputId} className={cn("digicode-input", className)} aria-invalid={!!error || undefined} {...props} />
23
+ {suffix && <span className="digicode-input__affix">{suffix}</span>}
24
+ </span>
25
+ {error ? <span className="digicode-field__error">{error}</span> : hint && <span className="digicode-field__hint">{hint}</span>}
26
+ </label>
27
+ );
28
+ });
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ export interface PaginationProps {
4
+ page: number;
5
+ totalPages: number;
6
+ onPageChange?: (page: number) => void;
7
+ className?: string;
8
+ }
9
+
10
+ export function Pagination({ page, totalPages, onPageChange, className }: PaginationProps) {
11
+ const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
12
+ return (
13
+ <nav aria-label="Pagination" className={className ? `digicode-pagination ${className}` : "digicode-pagination"}>
14
+ <button type="button" className="digicode-pagination__item" disabled={page <= 1} onClick={() => onPageChange?.(page - 1)} aria-label="Previous page">‹</button>
15
+ {pages.map((value) => (
16
+ <button key={value} type="button" className="digicode-pagination__item" data-active={page === value} aria-current={page === value ? "page" : undefined} onClick={() => onPageChange?.(value)}>{value}</button>
17
+ ))}
18
+ <button type="button" className="digicode-pagination__item" disabled={page >= totalPages} onClick={() => onPageChange?.(page + 1)} aria-label="Next page">›</button>
19
+ </nav>
20
+ );
21
+ }
@@ -0,0 +1,17 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface ProgressProps extends HTMLAttributes<HTMLDivElement> {
5
+ value: number;
6
+ max?: number;
7
+ label?: string;
8
+ }
9
+
10
+ export function Progress({ className, value, max = 100, label = "Progress", ...props }: ProgressProps) {
11
+ const percent = Math.max(0, Math.min(100, (value / max) * 100));
12
+ return (
13
+ <div className={cn("digicode-progress", className)} role="progressbar" aria-label={label} aria-valuenow={value} aria-valuemin={0} aria-valuemax={max} {...props}>
14
+ <div className="digicode-progress__bar" style={{ width: `${percent}%` }} />
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1,40 @@
1
+ "use client";
2
+
3
+ import type { InputHTMLAttributes } from "react";
4
+
5
+ export interface RadioOption {
6
+ label: string;
7
+ value: string;
8
+ disabled?: boolean;
9
+ }
10
+
11
+ export interface RadioGroupProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "onChange"> {
12
+ name: string;
13
+ value?: string;
14
+ defaultValue?: string;
15
+ options: RadioOption[];
16
+ onValueChange?: (value: string) => void;
17
+ }
18
+
19
+ export function RadioGroup({ name, value, defaultValue, options, onValueChange, ...props }: RadioGroupProps) {
20
+ return (
21
+ <div className="digicode-radio-group" role="radiogroup">
22
+ {options.map((option) => (
23
+ <label className="digicode-checkline" key={option.value}>
24
+ <input
25
+ type="radio"
26
+ className="digicode-radio"
27
+ name={name}
28
+ value={option.value}
29
+ checked={value !== undefined ? value === option.value : undefined}
30
+ defaultChecked={value === undefined ? defaultValue === option.value : undefined}
31
+ disabled={option.disabled}
32
+ onChange={() => onValueChange?.(option.value)}
33
+ {...props}
34
+ />
35
+ <span>{option.label}</span>
36
+ </label>
37
+ ))}
38
+ </div>
39
+ );
40
+ }
@@ -0,0 +1,33 @@
1
+ import { forwardRef, type SelectHTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface SelectOption {
5
+ label: string;
6
+ value: string;
7
+ disabled?: boolean;
8
+ }
9
+
10
+ export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
11
+ label?: string;
12
+ hint?: string;
13
+ error?: string;
14
+ options?: SelectOption[];
15
+ placeholder?: string;
16
+ }
17
+
18
+ export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(
19
+ { className, id, label, hint, error, options = [], placeholder, children, ...props },
20
+ ref
21
+ ) {
22
+ const selectId = id || props.name;
23
+ return (
24
+ <label className="digicode-field" htmlFor={selectId}>
25
+ {label && <span className="digicode-field__label">{label}</span>}
26
+ <select ref={ref} id={selectId} className={cn("digicode-select", className)} aria-invalid={!!error || undefined} {...props}>
27
+ {placeholder && <option value="">{placeholder}</option>}
28
+ {children || options.map((option) => <option key={option.value} value={option.value} disabled={option.disabled}>{option.label}</option>)}
29
+ </select>
30
+ {error ? <span className="digicode-field__error">{error}</span> : hint && <span className="digicode-field__hint">{hint}</span>}
31
+ </label>
32
+ );
33
+ });
@@ -0,0 +1,10 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface SeparatorProps extends HTMLAttributes<HTMLHRElement> {
5
+ orientation?: "horizontal" | "vertical";
6
+ }
7
+
8
+ export function Separator({ className, orientation = "horizontal", ...props }: SeparatorProps) {
9
+ return <hr role="separator" aria-orientation={orientation} className={cn("digicode-separator", `digicode-separator--${orientation}`, className)} {...props} />;
10
+ }
@@ -0,0 +1,6 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export function Skeleton({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
5
+ return <div aria-hidden="true" className={cn("digicode-skeleton", className)} {...props} />;
6
+ }
@@ -0,0 +1,11 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
5
+ size?: "sm" | "md" | "lg";
6
+ label?: string;
7
+ }
8
+
9
+ export function Spinner({ className, size = "md", label = "Loading", ...props }: SpinnerProps) {
10
+ return <span role="status" aria-label={label} className={cn("digicode-spinner", size !== "md" && `digicode-spinner--${size}`, className)} {...props} />;
11
+ }
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface StatCardProps {
5
+ label: ReactNode;
6
+ value: ReactNode;
7
+ icon?: ReactNode;
8
+ delta?: ReactNode;
9
+ trend?: "up" | "down" | "neutral";
10
+ className?: string;
11
+ }
12
+
13
+ export function StatCard({ label, value, icon, delta, trend = "neutral", className }: StatCardProps) {
14
+ return (
15
+ <div className={cn("digicode-stat", className)}>
16
+ <div className="digicode-stat__top"><span className="digicode-stat__label">{label}</span>{icon && <span className="digicode-stat__icon">{icon}</span>}</div>
17
+ <div className="digicode-stat__value">{value}</div>
18
+ {delta && <div className={cn("digicode-stat__delta", trend !== "neutral" && `digicode-stat__delta--${trend}`)}>{delta}</div>}
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,15 @@
1
+ import { forwardRef, type InputHTMLAttributes, type ReactNode } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
5
+ label?: ReactNode;
6
+ }
7
+
8
+ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch({ className, label, ...props }, ref) {
9
+ return (
10
+ <label className="digicode-switch-wrap">
11
+ <input ref={ref} type="checkbox" role="switch" className={cn("digicode-switch", className)} {...props} />
12
+ {label && <span>{label}</span>}
13
+ </label>
14
+ );
15
+ });
@@ -0,0 +1,11 @@
1
+ import type { ComponentPropsWithoutRef } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export function Table({ className, ...props }: ComponentPropsWithoutRef<"table">) {
5
+ return <div className="digicode-table-wrap"><table className={cn("digicode-table", className)} {...props} /></div>;
6
+ }
7
+ export function TableHeader(props: ComponentPropsWithoutRef<"thead">) { return <thead {...props} />; }
8
+ export function TableBody(props: ComponentPropsWithoutRef<"tbody">) { return <tbody {...props} />; }
9
+ export function TableRow(props: ComponentPropsWithoutRef<"tr">) { return <tr {...props} />; }
10
+ export function TableHead(props: ComponentPropsWithoutRef<"th">) { return <th {...props} />; }
11
+ export function TableCell(props: ComponentPropsWithoutRef<"td">) { return <td {...props} />; }
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ import { useMemo, useState, type ReactNode } from "react";
4
+
5
+ export interface TabItem {
6
+ value: string;
7
+ label: ReactNode;
8
+ content: ReactNode;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ export interface TabsProps {
13
+ items: TabItem[];
14
+ defaultValue?: string;
15
+ value?: string;
16
+ onValueChange?: (value: string) => void;
17
+ className?: string;
18
+ }
19
+
20
+ export function Tabs({ items, defaultValue, value, onValueChange, className }: TabsProps) {
21
+ const first = useMemo(() => items.find((item) => !item.disabled)?.value ?? "", [items]);
22
+ const [internal, setInternal] = useState(defaultValue ?? first);
23
+ const active = value ?? internal;
24
+ const current = items.find((item) => item.value === active) ?? items[0];
25
+
26
+ function select(next: string) {
27
+ if (value === undefined) setInternal(next);
28
+ onValueChange?.(next);
29
+ }
30
+
31
+ return (
32
+ <div className={className ? `digicode-tabs ${className}` : "digicode-tabs"}>
33
+ <div className="digicode-tabs__list" role="tablist">
34
+ {items.map((item) => (
35
+ <button
36
+ type="button"
37
+ role="tab"
38
+ key={item.value}
39
+ disabled={item.disabled}
40
+ aria-selected={active === item.value}
41
+ data-active={active === item.value}
42
+ className="digicode-tabs__trigger"
43
+ onClick={() => select(item.value)}
44
+ >
45
+ {item.label}
46
+ </button>
47
+ ))}
48
+ </div>
49
+ <div className="digicode-tabs__panel" role="tabpanel">{current?.content}</div>
50
+ </div>
51
+ );
52
+ }
@@ -0,0 +1,22 @@
1
+ import { forwardRef, type TextareaHTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
5
+ label?: string;
6
+ hint?: string;
7
+ error?: string;
8
+ }
9
+
10
+ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(
11
+ { className, id, label, hint, error, ...props },
12
+ ref
13
+ ) {
14
+ const textareaId = id || props.name;
15
+ return (
16
+ <label className="digicode-field" htmlFor={textareaId}>
17
+ {label && <span className="digicode-field__label">{label}</span>}
18
+ <textarea ref={ref} id={textareaId} className={cn("digicode-textarea", className)} aria-invalid={!!error || undefined} {...props} />
19
+ {error ? <span className="digicode-field__error">{error}</span> : hint && <span className="digicode-field__hint">{hint}</span>}
20
+ </label>
21
+ );
22
+ });
@@ -0,0 +1,16 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export interface TooltipProps {
4
+ content: ReactNode;
5
+ children: ReactNode;
6
+ className?: string;
7
+ }
8
+
9
+ export function Tooltip({ content, children, className }: TooltipProps) {
10
+ return (
11
+ <span className={className ? `digicode-tooltip ${className}` : "digicode-tooltip"}>
12
+ {children}
13
+ <span role="tooltip" className="digicode-tooltip__content">{content}</span>
14
+ </span>
15
+ );
16
+ }
@@ -0,0 +1,3 @@
1
+ export function cn(...values: Array<string | false | null | undefined>) {
2
+ return values.filter(Boolean).join(" ");
3
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "DigiCode",
3
+ "style": "digicode",
4
+ "components": [
5
+ "accordion",
6
+ "alert",
7
+ "avatar",
8
+ "badge",
9
+ "breadcrumb",
10
+ "button",
11
+ "card",
12
+ "checkbox",
13
+ "dialog",
14
+ "drawer",
15
+ "dropdown-menu",
16
+ "empty-state",
17
+ "input",
18
+ "pagination",
19
+ "progress",
20
+ "radio-group",
21
+ "select",
22
+ "separator",
23
+ "skeleton",
24
+ "spinner",
25
+ "stat-card",
26
+ "switch",
27
+ "table",
28
+ "tabs",
29
+ "textarea",
30
+ "tooltip"
31
+ ]
32
+ }
@@ -0,0 +1,253 @@
1
+ :root {
2
+ --digicode-ink: #1c2f41;
3
+ --digicode-heading: #12212e;
4
+ --digicode-muted: #60707e;
5
+ --digicode-primary: #2474ad;
6
+ --digicode-primary-hover: #1f6699;
7
+ --digicode-primary-soft: #eef6fb;
8
+ --digicode-surface: #ffffff;
9
+ --digicode-surface-2: #f4f7f9;
10
+ --digicode-line: #e4eaee;
11
+ --digicode-line-strong: #ced9e0;
12
+ --digicode-success: #21986c;
13
+ --digicode-warning: #c98217;
14
+ --digicode-danger: #c34a4a;
15
+ --digicode-info: #2474ad;
16
+ --digicode-ring: rgba(36, 116, 173, 0.2);
17
+ --digicode-radius-sm: 8px;
18
+ --digicode-radius: 12px;
19
+ --digicode-radius-lg: 18px;
20
+ --digicode-shadow-sm: 0 1px 2px rgba(18, 33, 46, 0.07);
21
+ --digicode-shadow: 0 8px 24px rgba(18, 33, 46, 0.08);
22
+ --digicode-shadow-lg: 0 20px 60px rgba(18, 33, 46, 0.16);
23
+ --digicode-ease: cubic-bezier(0.19, 1, 0.22, 1);
24
+ --digicode-font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
25
+ }
26
+
27
+ [data-digicode-theme="dark"] {
28
+ --digicode-ink: #e6eef4;
29
+ --digicode-heading: #f8fbfd;
30
+ --digicode-muted: #9babb8;
31
+ --digicode-primary: #5da4d1;
32
+ --digicode-primary-hover: #77b4da;
33
+ --digicode-primary-soft: #173247;
34
+ --digicode-surface: #0f1922;
35
+ --digicode-surface-2: #15232e;
36
+ --digicode-line: #253845;
37
+ --digicode-line-strong: #36505f;
38
+ --digicode-ring: rgba(93, 164, 209, 0.25);
39
+ --digicode-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
40
+ --digicode-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
41
+ --digicode-shadow-lg: 0 24px 70px rgba(0, 0, 0, 0.38);
42
+ }
43
+
44
+ .digicode-button,
45
+ .digicode-input,
46
+ .digicode-textarea,
47
+ .digicode-select,
48
+ .digicode-checkbox,
49
+ .digicode-radio,
50
+ .digicode-switch,
51
+ .digicode-badge,
52
+ .digicode-card,
53
+ .digicode-alert,
54
+ .digicode-avatar,
55
+ .digicode-tabs,
56
+ .digicode-accordion,
57
+ .digicode-dialog,
58
+ .digicode-drawer,
59
+ .digicode-dropdown,
60
+ .digicode-tooltip,
61
+ .digicode-breadcrumb,
62
+ .digicode-pagination,
63
+ .digicode-table,
64
+ .digicode-empty,
65
+ .digicode-stat {
66
+ font-family: var(--digicode-font);
67
+ box-sizing: border-box;
68
+ }
69
+
70
+ .digicode-button {
71
+ appearance: none;
72
+ border: 1px solid transparent;
73
+ border-radius: var(--digicode-radius);
74
+ min-height: 42px;
75
+ padding: 9px 18px;
76
+ display: inline-flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ gap: 9px;
80
+ font: 600 14px/1.2 var(--digicode-font);
81
+ white-space: nowrap;
82
+ cursor: pointer;
83
+ transition: transform .18s var(--digicode-ease), background .18s, border-color .18s, box-shadow .18s, color .18s;
84
+ }
85
+ .digicode-button:hover { transform: translateY(-1px); }
86
+ .digicode-button:active { transform: translateY(0); }
87
+ .digicode-button:focus-visible,
88
+ .digicode-input:focus-visible,
89
+ .digicode-textarea:focus-visible,
90
+ .digicode-select:focus-visible,
91
+ .digicode-checkbox:focus-visible,
92
+ .digicode-radio:focus-visible,
93
+ .digicode-switch:focus-visible,
94
+ .digicode-pagination__item:focus-visible {
95
+ outline: 3px solid var(--digicode-ring);
96
+ outline-offset: 2px;
97
+ }
98
+ .digicode-button:disabled { opacity: .55; cursor: not-allowed; transform: none; }
99
+ .digicode-button--primary { background: var(--digicode-ink); color: #fff; box-shadow: var(--digicode-shadow-sm); }
100
+ .digicode-button--primary:hover { background: #284458; box-shadow: 0 7px 18px rgba(28,47,65,.16); }
101
+ [data-digicode-theme="dark"] .digicode-button--primary { background: #e6eef4; color: #12212e; }
102
+ [data-digicode-theme="dark"] .digicode-button--primary:hover { background: #fff; }
103
+ .digicode-button--secondary { background: var(--digicode-primary); color: #fff; }
104
+ .digicode-button--secondary:hover { background: var(--digicode-primary-hover); }
105
+ .digicode-button--outline { background: var(--digicode-surface); color: var(--digicode-ink); border-color: var(--digicode-line-strong); }
106
+ .digicode-button--outline:hover { border-color: var(--digicode-primary); color: var(--digicode-primary); }
107
+ .digicode-button--ghost { background: transparent; color: var(--digicode-ink); }
108
+ .digicode-button--ghost:hover { background: var(--digicode-surface-2); }
109
+ .digicode-button--destructive { background: var(--digicode-danger); color: #fff; }
110
+ .digicode-button--link { padding-inline: 0; min-height: auto; background: transparent; color: var(--digicode-primary); border-radius: 0; }
111
+ .digicode-button--sm { min-height: 34px; padding: 7px 12px; font-size: 12px; border-radius: 9px; }
112
+ .digicode-button--lg { min-height: 50px; padding: 12px 22px; font-size: 15px; }
113
+ .digicode-button--icon { width: 42px; padding: 0; }
114
+ .digicode-button__spinner { width: 16px; height: 16px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: digicode-spin .7s linear infinite; }
115
+ @keyframes digicode-spin { to { transform: rotate(360deg); } }
116
+
117
+ .digicode-field { display: grid; gap: 7px; font-family: var(--digicode-font); }
118
+ .digicode-field__label { color: var(--digicode-heading); font-size: 13px; font-weight: 650; }
119
+ .digicode-field__hint { color: var(--digicode-muted); font-size: 12px; line-height: 1.45; }
120
+ .digicode-field__error { color: var(--digicode-danger); font-size: 12px; line-height: 1.45; }
121
+ .digicode-input-wrap { display: flex; align-items: center; gap: 8px; min-height: 44px; border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); background: var(--digicode-surface); color: var(--digicode-ink); padding-inline: 12px; transition: border-color .18s, box-shadow .18s, background .18s; }
122
+ .digicode-input-wrap:focus-within { border-color: var(--digicode-primary); box-shadow: 0 0 0 3px var(--digicode-ring); }
123
+ .digicode-input-wrap--error { border-color: var(--digicode-danger); }
124
+ .digicode-input { width: 100%; min-width: 0; border: 0; outline: 0; color: inherit; background: transparent; padding: 10px 0; font: 400 14px/1.4 var(--digicode-font); }
125
+ .digicode-input::placeholder, .digicode-textarea::placeholder { color: #93a0aa; }
126
+ .digicode-input__affix { display: inline-flex; align-items: center; color: var(--digicode-muted); font-size: 13px; }
127
+ .digicode-textarea { width: 100%; min-height: 112px; resize: vertical; border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); background: var(--digicode-surface); color: var(--digicode-ink); padding: 11px 12px; font: 400 14px/1.55 var(--digicode-font); transition: border-color .18s, box-shadow .18s; }
128
+ .digicode-textarea:focus { outline: 0; border-color: var(--digicode-primary); box-shadow: 0 0 0 3px var(--digicode-ring); }
129
+ .digicode-select { width: 100%; min-height: 44px; border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); background: var(--digicode-surface); color: var(--digicode-ink); padding: 0 36px 0 12px; font: 500 14px var(--digicode-font); }
130
+
131
+ .digicode-checkline { display: inline-flex; align-items: flex-start; gap: 10px; color: var(--digicode-ink); font: 500 14px/1.45 var(--digicode-font); cursor: pointer; }
132
+ .digicode-checkbox, .digicode-radio { width: 18px; height: 18px; flex: 0 0 18px; margin: 1px 0 0; accent-color: var(--digicode-primary); cursor: pointer; }
133
+ .digicode-radio-group { display: grid; gap: 10px; }
134
+ .digicode-switch-wrap { display: inline-flex; align-items: center; gap: 10px; color: var(--digicode-ink); font: 500 14px var(--digicode-font); cursor: pointer; }
135
+ .digicode-switch { position: relative; width: 42px; height: 24px; flex: 0 0 auto; appearance: none; border: 1px solid var(--digicode-line-strong); border-radius: 999px; background: #cfd8de; cursor: pointer; transition: background .2s, border-color .2s; }
136
+ .digicode-switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,.18); transition: transform .2s var(--digicode-ease); }
137
+ .digicode-switch:checked { background: var(--digicode-primary); border-color: var(--digicode-primary); }
138
+ .digicode-switch:checked::after { transform: translateX(18px); }
139
+
140
+ .digicode-badge { display: inline-flex; align-items: center; gap: 6px; width: fit-content; min-height: 24px; padding: 3px 9px; border-radius: 999px; background: var(--digicode-surface-2); color: var(--digicode-ink); border: 1px solid var(--digicode-line); font: 650 11px/1 var(--digicode-font); }
141
+ .digicode-badge--primary { color: var(--digicode-primary); background: var(--digicode-primary-soft); border-color: transparent; }
142
+ .digicode-badge--success { color: var(--digicode-success); background: rgba(33,152,108,.1); border-color: transparent; }
143
+ .digicode-badge--warning { color: var(--digicode-warning); background: rgba(201,130,23,.11); border-color: transparent; }
144
+ .digicode-badge--danger { color: var(--digicode-danger); background: rgba(195,74,74,.1); border-color: transparent; }
145
+
146
+ .digicode-card { border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius-lg); background: var(--digicode-surface); color: var(--digicode-ink); box-shadow: var(--digicode-shadow-sm); overflow: hidden; }
147
+ .digicode-card--elevated { box-shadow: var(--digicode-shadow); }
148
+ .digicode-card__header, .digicode-card__content, .digicode-card__footer { padding: 20px; }
149
+ .digicode-card__header { display: grid; gap: 6px; }
150
+ .digicode-card__content { padding-top: 0; }
151
+ .digicode-card__footer { border-top: 1px solid var(--digicode-line); display: flex; align-items: center; gap: 10px; }
152
+ .digicode-card__title { margin: 0; color: var(--digicode-heading); font: 650 17px/1.35 var(--digicode-font); }
153
+ .digicode-card__description { margin: 0; color: var(--digicode-muted); font: 400 13px/1.55 var(--digicode-font); }
154
+
155
+ .digicode-alert { display: flex; gap: 12px; border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); padding: 14px 15px; background: var(--digicode-surface); color: var(--digicode-ink); }
156
+ .digicode-alert--info { border-color: rgba(36,116,173,.25); background: rgba(36,116,173,.06); }
157
+ .digicode-alert--success { border-color: rgba(33,152,108,.25); background: rgba(33,152,108,.06); }
158
+ .digicode-alert--warning { border-color: rgba(201,130,23,.25); background: rgba(201,130,23,.06); }
159
+ .digicode-alert--danger { border-color: rgba(195,74,74,.25); background: rgba(195,74,74,.06); }
160
+ .digicode-alert__icon { margin-top: 2px; flex: 0 0 auto; }
161
+ .digicode-alert__title { margin: 0 0 3px; font: 650 13px/1.4 var(--digicode-font); }
162
+ .digicode-alert__description { margin: 0; color: var(--digicode-muted); font: 400 13px/1.5 var(--digicode-font); }
163
+
164
+ .digicode-avatar { display: inline-flex; align-items: center; justify-content: center; overflow: hidden; border-radius: 50%; background: var(--digicode-primary-soft); color: var(--digicode-primary); font: 700 13px var(--digicode-font); flex: 0 0 auto; }
165
+ .digicode-avatar img { width: 100%; height: 100%; object-fit: cover; }
166
+ .digicode-avatar--sm { width: 30px; height: 30px; }
167
+ .digicode-avatar--md { width: 40px; height: 40px; }
168
+ .digicode-avatar--lg { width: 52px; height: 52px; font-size: 16px; }
169
+
170
+ .digicode-spinner { display: inline-block; width: 24px; height: 24px; border: 3px solid var(--digicode-line); border-top-color: var(--digicode-primary); border-radius: 50%; animation: digicode-spin .75s linear infinite; }
171
+ .digicode-spinner--sm { width: 16px; height: 16px; border-width: 2px; }
172
+ .digicode-spinner--lg { width: 34px; height: 34px; border-width: 4px; }
173
+ .digicode-skeleton { display: block; border-radius: var(--digicode-radius-sm); background: linear-gradient(90deg, var(--digicode-surface-2) 25%, var(--digicode-line) 37%, var(--digicode-surface-2) 63%); background-size: 400% 100%; animation: digicode-shimmer 1.35s ease infinite; }
174
+ @keyframes digicode-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
175
+ .digicode-progress { width: 100%; height: 8px; overflow: hidden; border-radius: 999px; background: var(--digicode-surface-2); }
176
+ .digicode-progress__bar { height: 100%; border-radius: inherit; background: var(--digicode-primary); transition: width .3s var(--digicode-ease); }
177
+ .digicode-separator { border: 0; margin: 0; background: var(--digicode-line); }
178
+ .digicode-separator--horizontal { width: 100%; height: 1px; }
179
+ .digicode-separator--vertical { width: 1px; height: 100%; min-height: 18px; }
180
+
181
+ .digicode-tabs__list { display: inline-flex; align-items: center; gap: 3px; padding: 4px; background: var(--digicode-surface-2); border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); }
182
+ .digicode-tabs__trigger { appearance: none; border: 0; border-radius: 9px; background: transparent; color: var(--digicode-muted); padding: 8px 13px; font: 600 13px var(--digicode-font); cursor: pointer; }
183
+ .digicode-tabs__trigger[data-active="true"] { color: var(--digicode-heading); background: var(--digicode-surface); box-shadow: var(--digicode-shadow-sm); }
184
+ .digicode-tabs__panel { padding-top: 16px; color: var(--digicode-ink); }
185
+
186
+ .digicode-accordion { border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); background: var(--digicode-surface); overflow: hidden; }
187
+ .digicode-accordion__item + .digicode-accordion__item { border-top: 1px solid var(--digicode-line); }
188
+ .digicode-accordion__trigger { width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 15px 16px; border: 0; background: transparent; color: var(--digicode-heading); text-align: left; font: 650 14px var(--digicode-font); cursor: pointer; }
189
+ .digicode-accordion__chevron { transition: transform .2s; }
190
+ .digicode-accordion__trigger[aria-expanded="true"] .digicode-accordion__chevron { transform: rotate(180deg); }
191
+ .digicode-accordion__content { padding: 0 16px 16px; color: var(--digicode-muted); font: 400 13px/1.6 var(--digicode-font); }
192
+
193
+ .digicode-dialog-native { width: min(520px, calc(100vw - 32px)); max-width: none; border: 1px solid var(--digicode-line); border-radius: 20px; padding: 0; background: var(--digicode-surface); color: var(--digicode-ink); box-shadow: var(--digicode-shadow-lg); }
194
+ .digicode-dialog-native::backdrop { background: rgba(10, 22, 31, .56); backdrop-filter: blur(3px); }
195
+ .digicode-dialog__header, .digicode-dialog__body, .digicode-dialog__footer { padding: 20px; }
196
+ .digicode-dialog__header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; border-bottom: 1px solid var(--digicode-line); }
197
+ .digicode-dialog__title { margin: 0; color: var(--digicode-heading); font: 650 18px/1.4 var(--digicode-font); }
198
+ .digicode-dialog__description { margin: 5px 0 0; color: var(--digicode-muted); font: 400 13px/1.55 var(--digicode-font); }
199
+ .digicode-dialog__close { appearance: none; border: 0; border-radius: 8px; width: 32px; height: 32px; background: var(--digicode-surface-2); color: var(--digicode-muted); cursor: pointer; }
200
+ .digicode-dialog__footer { border-top: 1px solid var(--digicode-line); display: flex; justify-content: flex-end; gap: 9px; }
201
+
202
+ .digicode-drawer-native { margin: 0 0 0 auto; width: min(430px, 92vw); height: 100dvh; max-height: none; border: 0; border-left: 1px solid var(--digicode-line); border-radius: 20px 0 0 20px; padding: 0; background: var(--digicode-surface); color: var(--digicode-ink); box-shadow: var(--digicode-shadow-lg); }
203
+ .digicode-drawer-native::backdrop { background: rgba(10,22,31,.48); }
204
+ .digicode-drawer__header, .digicode-drawer__body, .digicode-drawer__footer { padding: 20px; }
205
+ .digicode-drawer__header { border-bottom: 1px solid var(--digicode-line); display: flex; justify-content: space-between; gap: 16px; }
206
+ .digicode-drawer__body { min-height: calc(100dvh - 150px); }
207
+ .digicode-drawer__footer { border-top: 1px solid var(--digicode-line); }
208
+
209
+ .digicode-dropdown { position: relative; display: inline-block; }
210
+ .digicode-dropdown > summary { list-style: none; }
211
+ .digicode-dropdown > summary::-webkit-details-marker { display: none; }
212
+ .digicode-dropdown__menu { position: absolute; z-index: 40; top: calc(100% + 7px); right: 0; width: 200px; padding: 6px; border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); background: var(--digicode-surface); box-shadow: var(--digicode-shadow); }
213
+ .digicode-dropdown__item { width: 100%; display: flex; align-items: center; gap: 9px; border: 0; border-radius: 8px; padding: 9px 10px; background: transparent; color: var(--digicode-ink); text-align: left; font: 500 13px var(--digicode-font); cursor: pointer; }
214
+ .digicode-dropdown__item:hover { background: var(--digicode-surface-2); }
215
+ .digicode-dropdown__item--danger { color: var(--digicode-danger); }
216
+ .digicode-dropdown__separator { height: 1px; margin: 5px 2px; background: var(--digicode-line); }
217
+ .digicode-dropdown__label { padding: 6px 10px; color: var(--digicode-muted); font: 650 11px var(--digicode-font); text-transform: uppercase; letter-spacing: .04em; }
218
+
219
+ .digicode-tooltip { position: relative; display: inline-flex; }
220
+ .digicode-tooltip__content { pointer-events: none; position: absolute; z-index: 60; left: 50%; bottom: calc(100% + 8px); transform: translateX(-50%) translateY(3px); opacity: 0; min-width: max-content; max-width: 240px; padding: 6px 8px; border-radius: 7px; background: var(--digicode-heading); color: #fff; font: 500 11px/1.35 var(--digicode-font); transition: opacity .15s, transform .15s; }
221
+ .digicode-tooltip:hover .digicode-tooltip__content, .digicode-tooltip:focus-within .digicode-tooltip__content { opacity: 1; transform: translateX(-50%) translateY(0); }
222
+
223
+ .digicode-breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; color: var(--digicode-muted); font: 500 12px var(--digicode-font); }
224
+ .digicode-breadcrumb a { color: inherit; text-decoration: none; }
225
+ .digicode-breadcrumb a:hover { color: var(--digicode-primary); }
226
+ .digicode-breadcrumb__current { color: var(--digicode-heading); }
227
+ .digicode-breadcrumb__sep { color: #a0aab1; }
228
+
229
+ .digicode-pagination { display: flex; align-items: center; gap: 5px; }
230
+ .digicode-pagination__item { min-width: 36px; height: 36px; border: 1px solid var(--digicode-line); border-radius: 9px; display: inline-flex; align-items: center; justify-content: center; background: var(--digicode-surface); color: var(--digicode-ink); font: 600 12px var(--digicode-font); cursor: pointer; }
231
+ .digicode-pagination__item:hover { border-color: var(--digicode-primary); color: var(--digicode-primary); }
232
+ .digicode-pagination__item[data-active="true"] { background: var(--digicode-ink); color: #fff; border-color: var(--digicode-ink); }
233
+
234
+ .digicode-table-wrap { overflow-x: auto; border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius); background: var(--digicode-surface); }
235
+ .digicode-table { width: 100%; border-collapse: collapse; color: var(--digicode-ink); font: 400 13px/1.5 var(--digicode-font); }
236
+ .digicode-table th { padding: 11px 14px; background: var(--digicode-surface-2); color: var(--digicode-muted); text-align: left; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .035em; }
237
+ .digicode-table td { padding: 13px 14px; border-top: 1px solid var(--digicode-line); }
238
+ .digicode-table tbody tr:hover { background: color-mix(in srgb, var(--digicode-surface-2) 60%, transparent); }
239
+
240
+ .digicode-empty { display: grid; place-items: center; text-align: center; gap: 8px; padding: 34px 24px; border: 1px dashed var(--digicode-line-strong); border-radius: var(--digicode-radius-lg); background: var(--digicode-surface); }
241
+ .digicode-empty__icon { width: 44px; height: 44px; display: grid; place-items: center; border-radius: 13px; background: var(--digicode-primary-soft); color: var(--digicode-primary); }
242
+ .digicode-empty__title { margin: 4px 0 0; color: var(--digicode-heading); font: 650 15px var(--digicode-font); }
243
+ .digicode-empty__description { margin: 0; max-width: 420px; color: var(--digicode-muted); font: 400 13px/1.55 var(--digicode-font); }
244
+ .digicode-empty__actions { margin-top: 6px; display: flex; gap: 8px; }
245
+
246
+ .digicode-stat { display: grid; gap: 9px; padding: 18px; border: 1px solid var(--digicode-line); border-radius: var(--digicode-radius-lg); background: var(--digicode-surface); box-shadow: var(--digicode-shadow-sm); }
247
+ .digicode-stat__top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
248
+ .digicode-stat__label { color: var(--digicode-muted); font: 600 12px var(--digicode-font); }
249
+ .digicode-stat__icon { color: var(--digicode-primary); }
250
+ .digicode-stat__value { color: var(--digicode-heading); font: 700 28px/1 var(--digicode-font); letter-spacing: -.025em; }
251
+ .digicode-stat__delta { color: var(--digicode-muted); font: 500 11px var(--digicode-font); }
252
+ .digicode-stat__delta--up { color: var(--digicode-success); }
253
+ .digicode-stat__delta--down { color: var(--digicode-danger); }