@bevlogic/style-system 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.
- package/CHANGELOG.md +23 -0
- package/README.md +65 -0
- package/brand-mark.svg +1 -0
- package/icons.json +93 -0
- package/icons.svg +83 -0
- package/package.json +35 -0
- package/recipes/app-shell.md +178 -0
- package/recipes/badge-pill.md +80 -0
- package/recipes/button.md +88 -0
- package/recipes/card.md +97 -0
- package/recipes/input.md +87 -0
- package/recipes/table.md +100 -0
- package/tokens.css +167 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@bevlogic/style-system` are recorded here. This package
|
|
4
|
+
follows [semver](https://semver.org/): consuming apps pin a version and upgrade
|
|
5
|
+
on their own schedule — a new major means "there's breaking work to do, do it
|
|
6
|
+
when you have cycles," not "everything is broken now."
|
|
7
|
+
|
|
8
|
+
## [0.1.0]
|
|
9
|
+
|
|
10
|
+
Initial release.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `tokens.css`: canonical design tokens (color palette, surfaces, text, brand,
|
|
14
|
+
an 8px spacing scale, a named type scale, radii, shadows) plus a restrained set
|
|
15
|
+
of gradient tokens (`--grad-*`) for sidebars, accents, surfaces, and panels.
|
|
16
|
+
- `brand-mark.svg` and the `--brand-mark` token (the mark as a CSS `mask` value),
|
|
17
|
+
so the brand mark can be painted in any token color from one source.
|
|
18
|
+
- `icons.svg`: an 80-symbol icon sprite (Lucide-derived), with a general-purpose
|
|
19
|
+
`ui` tier alongside the domain icons.
|
|
20
|
+
- `icons.json`: the icon catalog (label, category, "use for") so icons are picked
|
|
21
|
+
by intent.
|
|
22
|
+
- `recipes/`: core components (button, card, badge, input, table, app shell)
|
|
23
|
+
shown for React, static HTML, and PHP, all driven by the same tokens.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @bevlogic/style-system
|
|
2
|
+
|
|
3
|
+
The single source of truth for BevLogic UI: design tokens, the icon sprite, and
|
|
4
|
+
the brand mark. Consume these so interfaces stay visually consistent, and upgrade
|
|
5
|
+
on your own schedule via the package version.
|
|
6
|
+
|
|
7
|
+
## What's in here
|
|
8
|
+
|
|
9
|
+
| File | What it is |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `tokens.css` | Canonical CSS custom properties (color, spacing, type, radii, shadows, gradients). **The load-bearing file.** |
|
|
12
|
+
| `icons.svg` | Icon sprite (80 symbols), Lucide-derived. |
|
|
13
|
+
| `icons.json` | Icon catalog: label, category, and "use for" per icon. Pick by intent. |
|
|
14
|
+
| `brand-mark.svg` | The BevLogic brand mark (also available as the `--brand-mark` CSS token). |
|
|
15
|
+
| `recipes/` | Core components shown three ways: React, static HTML, and PHP. Same look, one token set. |
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
yarn add @bevlogic/style-system
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
@import "@bevlogic/style-system/tokens.css";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Subpath exports (`/tokens.css`, `/icons.svg`, `/icons.json`, `/brand-mark.svg`)
|
|
28
|
+
are declared in `package.json`. There is no `.` export, so always import a
|
|
29
|
+
subpath, not the bare package name.
|
|
30
|
+
|
|
31
|
+
Reference tokens by name:
|
|
32
|
+
|
|
33
|
+
```css
|
|
34
|
+
.button { background: var(--accent); color: var(--text-on-accent); }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Icons via the sprite:
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<svg><use href="/path/to/icons.svg#i-search" /></svg>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The brand mark can be painted with any color via the token:
|
|
44
|
+
|
|
45
|
+
```css
|
|
46
|
+
.brand-dot {
|
|
47
|
+
width: 20px; height: 20px;
|
|
48
|
+
background: var(--accent);
|
|
49
|
+
mask: var(--brand-mark) center / contain no-repeat;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Ground rules
|
|
54
|
+
|
|
55
|
+
1. **Consume tokens, don't fork values.** Need a value that isn't here? Add a
|
|
56
|
+
token upstream, don't hardcode a hex/px.
|
|
57
|
+
2. **8px spacing grid.** Prefer multiples of 8; `--space-half` (4px) is an escape
|
|
58
|
+
hatch, not a normal step.
|
|
59
|
+
3. **One icon system.** Use the sprite; don't hand-copy SVG paths.
|
|
60
|
+
|
|
61
|
+
## Versioning
|
|
62
|
+
|
|
63
|
+
Follows [semver](https://semver.org/): pin a version and upgrade when you have
|
|
64
|
+
cycles. A new major means there is breaking work to do, not that everything is
|
|
65
|
+
broken now.
|
package/brand-mark.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="#000"><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(0 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(18 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(36 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(54 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(72 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(90 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(108 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(126 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(144 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(162 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(180 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(198 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(216 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(234 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(252 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(270 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(288 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(306 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(324 50 50)"/><ellipse cx="50" cy="17" rx="3.2" ry="7" transform="rotate(342 50 50)"/></svg>
|
package/icons.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Canonical BevLogic icon catalog. Every id maps to a <symbol id=\"i-<id>\"> in icons.svg. Use this to pick the right icon by intent. Most icons are Lucide (ISC); 'lucide: null' means a custom glyph with no Lucide equivalent.",
|
|
3
|
+
"spriteUrl": "icons.svg",
|
|
4
|
+
"categories": {
|
|
5
|
+
"admin": "Internal admin shell — the staff-facing product.",
|
|
6
|
+
"client": "Customer portal — the client-facing surface.",
|
|
7
|
+
"util": "Utility / cross-cutting (navigation, fallback, auth state).",
|
|
8
|
+
"ui": "General-purpose interface glyphs — search, actions, disclosure, status. Stack-agnostic.",
|
|
9
|
+
"rcrv1": "Returns filing surface (RCRv1)."
|
|
10
|
+
},
|
|
11
|
+
"icons": [
|
|
12
|
+
{ "id": "dashboard", "label": "Dashboard", "category": "admin", "lucide": "layout-grid", "use": "Main admin dashboard / index landing page." },
|
|
13
|
+
{ "id": "inbox", "label": "Inbox", "category": "admin", "lucide": "inbox", "use": "Inbox of incoming tasks, messages, or items needing action." },
|
|
14
|
+
{ "id": "calendar", "label": "Calendar", "category": "admin", "lucide": "calendar-days", "use": "Filing calendar and upcoming deadlines." },
|
|
15
|
+
{ "id": "notifications", "label": "Notifications", "category": "admin", "lucide": "bell", "use": "Bell — system notifications, alerts, mentions." },
|
|
16
|
+
{ "id": "accounts", "label": "Accounts", "category": "admin", "lucide": "user", "use": "Account directory / customer list." },
|
|
17
|
+
{ "id": "returns", "label": "Returns", "category": "admin", "lucide": "repeat-2", "use": "Tax return filings — the Returns service module." },
|
|
18
|
+
{ "id": "registrations", "label": "Registrations", "category": "admin", "lucide": "file-text", "use": "State registration filings and status." },
|
|
19
|
+
{ "id": "licenses", "label": "Licenses", "category": "admin", "lucide": "id-card", "use": "License and permit tracking." },
|
|
20
|
+
{ "id": "filings", "label": "Filings", "category": "admin", "lucide": "folder", "use": "Generic filings module — uploads, drafts, submitted." },
|
|
21
|
+
{ "id": "reports", "label": "Reports", "category": "admin", "lucide": "chart-column-big", "use": "Reports, exports, analytics output." },
|
|
22
|
+
{ "id": "settings", "label": "Settings", "category": "admin", "lucide": "settings", "use": "Workspace, tenant, and personal configuration." },
|
|
23
|
+
{ "id": "ingestion", "label": "Ingestion", "category": "admin", "lucide": "cloud-upload", "use": "Data ingest pipelines — file uploads, agency feeds." },
|
|
24
|
+
{ "id": "rates", "label": "Rates Explorer", "category": "admin", "lucide": "circle-dollar-sign", "use": "Tax rates database explorer." },
|
|
25
|
+
{ "id": "notices", "label": "Notices", "category": "admin", "lucide": "mail", "use": "Notice triage — agency letters, response queue." },
|
|
26
|
+
{ "id": "filing-preview", "label": "Filing Preview", "category": "admin", "lucide": "file-search", "use": "Preview a generated filing PDF before submission." },
|
|
27
|
+
{ "id": "integration-health", "label": "Integration Health", "category": "admin", "lucide": "activity", "use": "Connector / external integration status." },
|
|
28
|
+
{ "id": "finance", "label": "Finance", "category": "admin", "lucide": "banknote", "use": "Finance dashboard — AR, AP, accounting." },
|
|
29
|
+
{ "id": "reconciliation", "label": "Reconciliation", "category": "admin", "lucide": "scale", "use": "Reconcile finance ledger against filings." },
|
|
30
|
+
{ "id": "quickbooks", "label": "QuickBooks", "category": "admin", "lucide": null, "use": "QuickBooks integration page. Custom — no Lucide equivalent." },
|
|
31
|
+
{ "id": "discrepancies", "label": "Discrepancies", "category": "admin", "lucide": "alert-triangle", "use": "Discrepancy queue — mismatches needing review." },
|
|
32
|
+
{ "id": "period-close", "label": "Period Close", "category": "admin", "lucide": "calendar-check", "use": "Monthly / period close checklist." },
|
|
33
|
+
{ "id": "client-dashboard", "label": "Client Dashboard", "category": "client", "lucide": "gauge", "use": "Customer portal landing." },
|
|
34
|
+
{ "id": "client-status", "label": "Client Filing Status", "category": "client", "lucide": "workflow", "use": "Customer view of their filing status." },
|
|
35
|
+
{ "id": "client-registrations", "label": "Client Registrations", "category": "client", "lucide": "file-text", "use": "Customer view of their state registrations." },
|
|
36
|
+
{ "id": "client-licenses", "label": "Client Licenses", "category": "client", "lucide": "id-card", "use": "Customer view of their licenses and permits." },
|
|
37
|
+
{ "id": "client-notices", "label": "Client Notices", "category": "client", "lucide": "mail", "use": "Customer-facing notices inbox." },
|
|
38
|
+
{ "id": "client-renewals", "label": "Client Renewals", "category": "client", "lucide": "rotate-cw", "use": "Upcoming renewal actions for the customer." },
|
|
39
|
+
{ "id": "client-products", "label": "Client Products", "category": "client", "lucide": "package", "use": "Customer product catalog / SKUs." },
|
|
40
|
+
{ "id": "client-distributors", "label": "Client Distributors", "category": "client", "lucide": "truck", "use": "Distributor relationships." },
|
|
41
|
+
{ "id": "client-billing", "label": "Client Billing", "category": "client", "lucide": "receipt-text", "use": "Customer invoices, billing history." },
|
|
42
|
+
{ "id": "client-documents", "label": "Client Documents", "category": "client", "lucide": "file-text", "use": "Customer document vault — uploads, signed forms." },
|
|
43
|
+
{ "id": "client-help", "label": "Client Help", "category": "client", "lucide": "help-circle", "use": "Customer help / support center." },
|
|
44
|
+
{ "id": "back-to-admin", "label": "Back to Admin", "category": "util", "lucide": "arrow-left", "use": "Switch from impersonating client back to admin shell." },
|
|
45
|
+
{ "id": "default", "label": "Default", "category": "util", "lucide": "dot", "use": "Fallback symbol when a key is missing from the sprite." },
|
|
46
|
+
{ "id": "lock-keyhole", "label": "Lock", "category": "util", "lucide": "lock-keyhole", "use": "Locked / authenticated state — secure access, restricted resource." },
|
|
47
|
+
{ "id": "liability-summary", "label": "Liability Summary", "category": "rcrv1", "lucide": "calculator", "use": "RCRv1 — tax liability summary view." },
|
|
48
|
+
{ "id": "filing-tasks", "label": "Filing Tasks", "category": "rcrv1", "lucide": "list-checks", "use": "RCRv1 — return filing task list." },
|
|
49
|
+
{ "id": "transactions", "label": "Transactions", "category": "rcrv1", "lucide": "arrow-left-right", "use": "RCRv1 — transaction browser." },
|
|
50
|
+
{ "id": "import-history", "label": "Import History", "category": "rcrv1", "lucide": "history", "use": "RCRv1 — file import log." },
|
|
51
|
+
{ "id": "visual-form-editor", "label": "Form Editor", "category": "rcrv1", "lucide": "pencil-ruler", "use": "RCRv1 — visual form layout editor." },
|
|
52
|
+
{ "id": "chevron-down", "label": "Chevron Down", "category": "ui", "lucide": "chevron-down", "use": "Disclosure caret — dropdown, accordion, select. Replaces CSS-triangle carets." },
|
|
53
|
+
{ "id": "chevron-up", "label": "Chevron Up", "category": "ui", "lucide": "chevron-up", "use": "Collapse an expanded disclosure; scroll-to-top affordance." },
|
|
54
|
+
{ "id": "chevron-left", "label": "Chevron Left", "category": "ui", "lucide": "chevron-left", "use": "Previous page / step in pagination and carousels." },
|
|
55
|
+
{ "id": "chevron-right", "label": "Chevron Right", "category": "ui", "lucide": "chevron-right", "use": "Next page / step; drill-in affordance on list rows." },
|
|
56
|
+
{ "id": "arrow-left", "label": "Arrow Left", "category": "ui", "lucide": "arrow-left", "use": "Back navigation. (Domain twin: `back-to-admin`.)" },
|
|
57
|
+
{ "id": "arrow-right", "label": "Arrow Right", "category": "ui", "lucide": "arrow-right", "use": "Forward navigation; 'continue' on a wizard step." },
|
|
58
|
+
{ "id": "sort", "label": "Sort", "category": "ui", "lucide": "arrow-up-down", "use": "Sortable table column header (unsorted state)." },
|
|
59
|
+
{ "id": "search", "label": "Search", "category": "ui", "lucide": "search", "use": "Search input adornment, filter-by-text affordance." },
|
|
60
|
+
{ "id": "plus", "label": "Add", "category": "ui", "lucide": "plus", "use": "Add / create — 'Add rate', 'New jurisdiction' buttons." },
|
|
61
|
+
{ "id": "x", "label": "Close", "category": "ui", "lucide": "x", "use": "Close a drawer, modal, toast, or dismiss a chip." },
|
|
62
|
+
{ "id": "check", "label": "Check", "category": "ui", "lucide": "check", "use": "Confirm / done / selected item in a menu or list." },
|
|
63
|
+
{ "id": "copy", "label": "Copy", "category": "ui", "lucide": "copy", "use": "Copy a value to the clipboard — ids, JSON payloads." },
|
|
64
|
+
{ "id": "pencil", "label": "Edit", "category": "ui", "lucide": "pencil", "use": "Edit an existing record inline or in a drawer." },
|
|
65
|
+
{ "id": "trash", "label": "Delete", "category": "ui", "lucide": "trash-2", "use": "Delete / remove a record. Pair with `--danger`." },
|
|
66
|
+
{ "id": "download", "label": "Download", "category": "ui", "lucide": "download", "use": "Export or download a file — CSV, PDF, report." },
|
|
67
|
+
{ "id": "upload", "label": "Upload", "category": "ui", "lucide": "upload", "use": "Upload a file — imports, document attachments." },
|
|
68
|
+
{ "id": "external-link", "label": "External Link", "category": "ui", "lucide": "external-link", "use": "Link that opens off-app or in a new tab." },
|
|
69
|
+
{ "id": "filter", "label": "Filter", "category": "ui", "lucide": "filter", "use": "Open filter controls for a table or list." },
|
|
70
|
+
{ "id": "more", "label": "More", "category": "ui", "lucide": "ellipsis", "use": "Overflow / kebab menu of secondary row actions." },
|
|
71
|
+
{ "id": "refresh", "label": "Refresh", "category": "ui", "lucide": "rotate-cw", "use": "Re-fetch or recalculate the current view." },
|
|
72
|
+
{ "id": "log-out", "label": "Log Out", "category": "ui", "lucide": "log-out", "use": "Sign out — user menu logout item." },
|
|
73
|
+
{ "id": "eye", "label": "Show", "category": "ui", "lucide": "eye", "use": "Reveal a hidden value; toggle row visibility." },
|
|
74
|
+
{ "id": "eye-off", "label": "Hide", "category": "ui", "lucide": "eye-off", "use": "Hide a revealed value; the off state of `eye`." },
|
|
75
|
+
{ "id": "info", "label": "Info", "category": "ui", "lucide": "info", "use": "Informational note or tooltip trigger. Pair with `--info`." },
|
|
76
|
+
{ "id": "alert-triangle", "label": "Warning", "category": "ui", "lucide": "triangle-alert", "use": "Warning callout / validation caution. Pair with `--warning`." },
|
|
77
|
+
{ "id": "check-circle", "label": "Success", "category": "ui", "lucide": "circle-check", "use": "Success confirmation in a toast or banner. Pair with `--success`." },
|
|
78
|
+
{ "id": "x-circle", "label": "Error", "category": "ui", "lucide": "circle-x", "use": "Error state in a toast or banner. Pair with `--danger`." },
|
|
79
|
+
{ "id": "clock", "label": "Pending", "category": "ui", "lucide": "clock", "use": "Pending / scheduled / awaiting-action state." },
|
|
80
|
+
{ "id": "spinner", "label": "Loading", "category": "ui", "lucide": "loader-circle", "use": "In-flight state; animate with a CSS spin." },
|
|
81
|
+
{ "id": "package", "label": "Package", "category": "ui", "lucide": "package", "use": "Generic product / SKU / shipment. (Domain twin: `client-products`.)" },
|
|
82
|
+
{ "id": "truck", "label": "Truck", "category": "ui", "lucide": "truck", "use": "Generic distribution / shipping. (Domain twin: `client-distributors`.)" },
|
|
83
|
+
{ "id": "history", "label": "History", "category": "ui", "lucide": "history", "use": "Generic audit trail / change log. (Domain twin: `import-history`.)" },
|
|
84
|
+
{ "id": "folder", "label": "Folder", "category": "ui", "lucide": "folder", "use": "Generic document or file grouping. (Domain twin: `filings`.)" },
|
|
85
|
+
{ "id": "chart-bar", "label": "Bar Chart", "category": "ui", "lucide": "chart-column-big", "use": "Generic analytics / reporting tile. (Domain twin: `reports`.)" },
|
|
86
|
+
{ "id": "shield", "label": "Compliance", "category": "ui", "lucide": "shield", "use": "Compliance / protection / permissions section." },
|
|
87
|
+
{ "id": "route", "label": "Route", "category": "ui", "lucide": "route", "use": "Multi-step flow, distribution path, pipeline." },
|
|
88
|
+
{ "id": "globe", "label": "Jurisdictions", "category": "ui", "lucide": "globe", "use": "Geography — states, jurisdictions, territories." },
|
|
89
|
+
{ "id": "users", "label": "Users", "category": "ui", "lucide": "users", "use": "A group of people — teams, contacts, staff list. (Single user: `accounts`.)" },
|
|
90
|
+
{ "id": "sliders", "label": "Controls", "category": "ui", "lucide": "sliders-horizontal","use": "Tunable settings / configuration sliders. (Gear: `settings`.)" },
|
|
91
|
+
{ "id": "plug", "label": "Integrations", "category": "ui", "lucide": "plug", "use": "External connector / integration setup. (Status: `integration-health`.)" }
|
|
92
|
+
]
|
|
93
|
+
}
|
package/icons.svg
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
|
2
|
+
<symbol id="i-dashboard" viewBox="0 0 24 24"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
|
3
|
+
<symbol id="i-inbox" viewBox="0 0 24 24"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" /> <path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /></symbol>
|
|
4
|
+
<symbol id="i-calendar" viewBox="0 0 24 24"><path d="M8 2v4" /> <path d="M16 2v4" /> <rect width="18" height="18" x="3" y="4" rx="2" /> <path d="M3 10h18" /> <path d="M8 14h.01" /> <path d="M12 14h.01" /> <path d="M16 14h.01" /> <path d="M8 18h.01" /> <path d="M12 18h.01" /> <path d="M16 18h.01" /></symbol>
|
|
5
|
+
<symbol id="i-notifications" viewBox="0 0 24 24"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
|
6
|
+
<symbol id="i-accounts" viewBox="0 0 24 24"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /> <circle cx="12" cy="7" r="4" /></symbol>
|
|
7
|
+
<symbol id="i-returns" viewBox="0 0 24 24"><path d="m2 9 3-3 3 3" /> <path d="M13 18H7a2 2 0 0 1-2-2V6" /> <path d="m22 15-3 3-3-3" /> <path d="M11 6h6a2 2 0 0 1 2 2v10" /></symbol>
|
|
8
|
+
<symbol id="i-registrations" viewBox="0 0 24 24"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
|
9
|
+
<symbol id="i-licenses" viewBox="0 0 24 24"><path d="M16 10h2" /> <path d="M16 14h2" /> <path d="M6.17 15a3 3 0 0 1 5.66 0" /> <circle cx="9" cy="11" r="2" /> <rect x="2" y="5" width="20" height="14" rx="2" /></symbol>
|
|
10
|
+
<symbol id="i-filings" viewBox="0 0 24 24"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></symbol>
|
|
11
|
+
<symbol id="i-reports" viewBox="0 0 24 24"><path d="M3 3v16a2 2 0 0 0 2 2h16" /> <rect x="15" y="5" width="4" height="12" rx="1" /> <rect x="7" y="8" width="4" height="9" rx="1" /></symbol>
|
|
12
|
+
<symbol id="i-settings" viewBox="0 0 24 24"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
|
13
|
+
<symbol id="i-ingestion" viewBox="0 0 24 24"><path d="M12 13v8" /> <path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" /> <path d="m8 17 4-4 4 4" /></symbol>
|
|
14
|
+
<symbol id="i-rates" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8" /> <path d="M12 18V6" /></symbol>
|
|
15
|
+
<symbol id="i-notices" viewBox="0 0 24 24"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" /> <rect x="2" y="4" width="20" height="16" rx="2" /></symbol>
|
|
16
|
+
<symbol id="i-filing-preview" viewBox="0 0 24 24"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <circle cx="11.5" cy="14.5" r="2.5" /> <path d="M13.3 16.3 15 18" /></symbol>
|
|
17
|
+
<symbol id="i-integration-health" viewBox="0 0 24 24"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" /></symbol>
|
|
18
|
+
<symbol id="i-finance" viewBox="0 0 24 24"><rect width="20" height="12" x="2" y="6" rx="2" /> <circle cx="12" cy="12" r="2" /> <path d="M6 12h.01M18 12h.01" /></symbol>
|
|
19
|
+
<symbol id="i-reconciliation" viewBox="0 0 24 24"><path d="M12 3v18" /> <path d="m19 8 3 8a5 5 0 0 1-6 0zV7" /> <path d="M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1" /> <path d="m5 8 3 8a5 5 0 0 1-6 0zV7" /> <path d="M7 21h10" /></symbol>
|
|
20
|
+
<symbol id="i-quickbooks" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9 9a3 3 0 0 1 3-3v6"/><path d="M15 15a3 3 0 0 1-3 3v-6"/></symbol>
|
|
21
|
+
<symbol id="i-discrepancies" viewBox="0 0 24 24"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
|
22
|
+
<symbol id="i-period-close" viewBox="0 0 24 24"><path d="M8 2v4" /> <path d="M16 2v4" /> <rect width="18" height="18" x="3" y="4" rx="2" /> <path d="M3 10h18" /> <path d="m9 16 2 2 4-4" /></symbol>
|
|
23
|
+
<symbol id="i-client-dashboard" viewBox="0 0 24 24"><path d="m12 14 4-4" /> <path d="M3.34 19a10 10 0 1 1 17.32 0" /></symbol>
|
|
24
|
+
<symbol id="i-client-status" viewBox="0 0 24 24"><rect width="8" height="8" x="3" y="3" rx="2" /> <path d="M7 11v4a2 2 0 0 0 2 2h4" /> <rect width="8" height="8" x="13" y="13" rx="2" /></symbol>
|
|
25
|
+
<symbol id="i-client-registrations" viewBox="0 0 24 24"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
|
26
|
+
<symbol id="i-client-licenses" viewBox="0 0 24 24"><path d="M16 10h2" /> <path d="M16 14h2" /> <path d="M6.17 15a3 3 0 0 1 5.66 0" /> <circle cx="9" cy="11" r="2" /> <rect x="2" y="5" width="20" height="14" rx="2" /></symbol>
|
|
27
|
+
<symbol id="i-client-notices" viewBox="0 0 24 24"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" /> <rect x="2" y="4" width="20" height="16" rx="2" /></symbol>
|
|
28
|
+
<symbol id="i-client-renewals" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
|
29
|
+
<symbol id="i-client-products" viewBox="0 0 24 24"><path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" /> <path d="M12 22V12" /> <polyline points="3.29 7 12 12 20.71 7" /> <path d="m7.5 4.27 9 5.15" /></symbol>
|
|
30
|
+
<symbol id="i-client-distributors" viewBox="0 0 24 24"><path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2" /> <path d="M15 18H9" /> <path d="M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14" /> <circle cx="17" cy="18" r="2" /> <circle cx="7" cy="18" r="2" /></symbol>
|
|
31
|
+
<symbol id="i-client-billing" viewBox="0 0 24 24"><path d="M13 16H8" /> <path d="M14 8H8" /> <path d="M16 12H8" /> <path d="M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" /></symbol>
|
|
32
|
+
<symbol id="i-client-documents" viewBox="0 0 24 24"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
|
33
|
+
<symbol id="i-client-help" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /> <path d="M12 17h.01" /></symbol>
|
|
34
|
+
<symbol id="i-back-to-admin" viewBox="0 0 24 24"><path d="m12 19-7-7 7-7" /> <path d="M19 12H5" /></symbol>
|
|
35
|
+
<symbol id="i-default" viewBox="0 0 24 24"><circle cx="12.1" cy="12.1" r="1" /></symbol>
|
|
36
|
+
<symbol id="i-lock-keyhole" viewBox="0 0 24 24"><circle cx="12" cy="16" r="1"/><rect x="3" y="10" width="18" height="12" rx="2"/><path d="M7 10V7a5 5 0 0 1 10 0v3"/></symbol>
|
|
37
|
+
<symbol id="i-liability-summary" viewBox="0 0 24 24"><rect width="16" height="20" x="4" y="2" rx="2"/><line x1="8" x2="16" y1="6" y2="6"/><line x1="16" x2="16" y1="14" y2="18"/><path d="M16 10h.01"/><path d="M12 10h.01"/><path d="M8 10h.01"/><path d="M12 14h.01"/><path d="M8 14h.01"/><path d="M12 18h.01"/><path d="M8 18h.01"/></symbol>
|
|
38
|
+
<symbol id="i-filing-tasks" viewBox="0 0 24 24"><path d="M13 5h8"/><path d="M13 12h8"/><path d="M13 19h8"/><path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/></symbol>
|
|
39
|
+
<symbol id="i-transactions" viewBox="0 0 24 24"><path d="M8 3 4 7l4 4"/><path d="M4 7h16"/><path d="m16 21 4-4-4-4"/><path d="M20 17H4"/></symbol>
|
|
40
|
+
<symbol id="i-import-history" viewBox="0 0 24 24"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/></symbol>
|
|
41
|
+
<symbol id="i-visual-form-editor" viewBox="0 0 24 24"><path d="M13 7 8.7 2.7a2.41 2.41 0 0 0-3.4 0L2.7 5.3a2.41 2.41 0 0 0 0 3.4L7 13"/><path d="m8 6 2-2"/><path d="m18 16 2-2"/><path d="m17 11 4.3 4.3c.94.94.94 2.46 0 3.4l-2.6 2.6c-.94.94-2.46.94-3.4 0L11 17"/><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></symbol>
|
|
42
|
+
<!-- ui: general-purpose interface glyphs (search, actions, disclosure, status) -->
|
|
43
|
+
<symbol id="i-chevron-down" viewBox="0 0 24 24"><path d="m6 9 6 6 6-6" /></symbol>
|
|
44
|
+
<symbol id="i-chevron-up" viewBox="0 0 24 24"><path d="m18 15-6-6-6 6" /></symbol>
|
|
45
|
+
<symbol id="i-chevron-left" viewBox="0 0 24 24"><path d="m15 18-6-6 6-6" /></symbol>
|
|
46
|
+
<symbol id="i-chevron-right" viewBox="0 0 24 24"><path d="m9 18 6-6-6-6" /></symbol>
|
|
47
|
+
<symbol id="i-arrow-left" viewBox="0 0 24 24"><path d="m12 19-7-7 7-7" /> <path d="M19 12H5" /></symbol>
|
|
48
|
+
<symbol id="i-arrow-right" viewBox="0 0 24 24"><path d="M5 12h14" /> <path d="m12 5 7 7-7 7" /></symbol>
|
|
49
|
+
<symbol id="i-sort" viewBox="0 0 24 24"><path d="m21 16-4 4-4-4" /> <path d="M17 20V4" /> <path d="m3 8 4-4 4 4" /> <path d="M7 4v16" /></symbol>
|
|
50
|
+
<symbol id="i-search" viewBox="0 0 24 24"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
|
51
|
+
<symbol id="i-plus" viewBox="0 0 24 24"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
|
52
|
+
<symbol id="i-x" viewBox="0 0 24 24"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
|
53
|
+
<symbol id="i-check" viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5" /></symbol>
|
|
54
|
+
<symbol id="i-copy" viewBox="0 0 24 24"><rect width="14" height="14" x="8" y="8" rx="2" ry="2" /> <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /></symbol>
|
|
55
|
+
<symbol id="i-pencil" viewBox="0 0 24 24"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
|
56
|
+
<symbol id="i-trash" viewBox="0 0 24 24"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
|
57
|
+
<symbol id="i-download" viewBox="0 0 24 24"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
|
58
|
+
<symbol id="i-upload" viewBox="0 0 24 24"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
|
59
|
+
<symbol id="i-external-link" viewBox="0 0 24 24"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
|
60
|
+
<symbol id="i-filter" viewBox="0 0 24 24"><path d="M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z" /></symbol>
|
|
61
|
+
<symbol id="i-more" viewBox="0 0 24 24"><circle cx="12" cy="12" r="1" /> <circle cx="19" cy="12" r="1" /> <circle cx="5" cy="12" r="1" /></symbol>
|
|
62
|
+
<symbol id="i-refresh" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
|
63
|
+
<symbol id="i-log-out" viewBox="0 0 24 24"><path d="m16 17 5-5-5-5" /> <path d="M21 12H9" /> <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" /></symbol>
|
|
64
|
+
<symbol id="i-eye" viewBox="0 0 24 24"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" /> <circle cx="12" cy="12" r="3" /></symbol>
|
|
65
|
+
<symbol id="i-eye-off" viewBox="0 0 24 24"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" /> <path d="M14.084 14.158a3 3 0 0 1-4.242-4.242" /> <path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" /> <path d="m2 2 20 20" /></symbol>
|
|
66
|
+
<symbol id="i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
|
67
|
+
<symbol id="i-alert-triangle" viewBox="0 0 24 24"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
|
68
|
+
<symbol id="i-check-circle" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="m9 12 2 2 4-4" /></symbol>
|
|
69
|
+
<symbol id="i-x-circle" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="m15 9-6 6" /> <path d="m9 9 6 6" /></symbol>
|
|
70
|
+
<symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="M12 6v6l4 2" /></symbol>
|
|
71
|
+
<symbol id="i-spinner" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-6.219-8.56" /></symbol>
|
|
72
|
+
<symbol id="i-package" viewBox="0 0 24 24"><path d="M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" /> <path d="M12 22V12" /> <polyline points="3.29 7 12 12 20.71 7" /> <path d="m7.5 4.27 9 5.15" /></symbol>
|
|
73
|
+
<symbol id="i-truck" viewBox="0 0 24 24"><path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2" /> <path d="M15 18H9" /> <path d="M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14" /> <circle cx="17" cy="18" r="2" /> <circle cx="7" cy="18" r="2" /></symbol>
|
|
74
|
+
<symbol id="i-history" viewBox="0 0 24 24"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /> <path d="M3 3v5h5" /> <path d="M12 7v5l4 2" /></symbol>
|
|
75
|
+
<symbol id="i-folder" viewBox="0 0 24 24"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></symbol>
|
|
76
|
+
<symbol id="i-chart-bar" viewBox="0 0 24 24"><path d="M3 3v16a2 2 0 0 0 2 2h16" /> <rect x="15" y="5" width="4" height="12" rx="1" /> <rect x="7" y="8" width="4" height="9" rx="1" /></symbol>
|
|
77
|
+
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
|
78
|
+
<symbol id="i-route" viewBox="0 0 24 24"><circle cx="6" cy="19" r="3" /> <path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15" /> <circle cx="18" cy="5" r="3" /></symbol>
|
|
79
|
+
<symbol id="i-globe" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> <path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" /> <path d="M2 12h20" /></symbol>
|
|
80
|
+
<symbol id="i-users" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /> <path d="M16 3.128a4 4 0 0 1 0 7.744" /> <path d="M22 21v-2a4 4 0 0 0-3-3.87" /> <circle cx="9" cy="7" r="4" /></symbol>
|
|
81
|
+
<symbol id="i-sliders" viewBox="0 0 24 24"><path d="M10 5H3" /> <path d="M12 19H3" /> <path d="M14 3v4" /> <path d="M16 17v4" /> <path d="M21 12h-9" /> <path d="M21 19h-5" /> <path d="M21 5h-7" /> <path d="M8 10v4" /> <path d="M8 12H3" /></symbol>
|
|
82
|
+
<symbol id="i-plug" viewBox="0 0 24 24"><path d="M12 22v-5" /> <path d="M15 8V2" /> <path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z" /> <path d="M9 8V2" /></symbol>
|
|
83
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bevlogic/style-system",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BevLogic shared design tokens, icon sprite, and brand mark. The single source of truth for consistent BevLogic UI.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/BevLogic/style-system.git"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"tokens.css",
|
|
13
|
+
"icons.svg",
|
|
14
|
+
"icons.json",
|
|
15
|
+
"brand-mark.svg",
|
|
16
|
+
"recipes",
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
"./tokens.css": "./tokens.css",
|
|
22
|
+
"./icons.svg": "./icons.svg",
|
|
23
|
+
"./icons.json": "./icons.json",
|
|
24
|
+
"./brand-mark.svg": "./brand-mark.svg"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"design-system",
|
|
31
|
+
"design-tokens",
|
|
32
|
+
"bevlogic",
|
|
33
|
+
"style-guide"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Recipe: App shell
|
|
2
|
+
|
|
3
|
+
The canonical layout: **fixed navy left sidebar + sticky topbar + centered
|
|
4
|
+
content**, a CSS grid. Requires `tokens.css` and `icons.svg`.
|
|
5
|
+
|
|
6
|
+
## CSS (shared)
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
.app {
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: var(--sidebar-width) 1fr;
|
|
12
|
+
min-height: 100vh;
|
|
13
|
+
transition: grid-template-columns 0.18s ease;
|
|
14
|
+
}
|
|
15
|
+
.app.sidebar-collapsed { grid-template-columns: var(--sidebar-collapsed-width) 1fr; }
|
|
16
|
+
|
|
17
|
+
.sidebar {
|
|
18
|
+
background: var(--sidebar-bg);
|
|
19
|
+
color: var(--sidebar-fg);
|
|
20
|
+
padding: 18px 12px;
|
|
21
|
+
position: sticky;
|
|
22
|
+
top: 0;
|
|
23
|
+
height: 100vh;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
overflow: auto;
|
|
27
|
+
z-index: 20;
|
|
28
|
+
}
|
|
29
|
+
.brand {
|
|
30
|
+
font-weight: var(--fw-semibold);
|
|
31
|
+
font-size: 16px;
|
|
32
|
+
padding: 6px 10px 18px;
|
|
33
|
+
letter-spacing: 0.3px;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: var(--space-1);
|
|
37
|
+
}
|
|
38
|
+
.brand .brand-dot {
|
|
39
|
+
width: 20px; height: 20px;
|
|
40
|
+
background: var(--accent);
|
|
41
|
+
-webkit-mask: var(--brand-mark) center / contain no-repeat;
|
|
42
|
+
mask: var(--brand-mark) center / contain no-repeat;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.nav-section {
|
|
46
|
+
font-size: 11px;
|
|
47
|
+
text-transform: uppercase;
|
|
48
|
+
letter-spacing: 0.8px;
|
|
49
|
+
color: var(--sidebar-fg-faint);
|
|
50
|
+
padding: 14px 10px 6px;
|
|
51
|
+
}
|
|
52
|
+
.nav { list-style: none; padding: 0; margin: 0; }
|
|
53
|
+
.nav li a {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 11px;
|
|
57
|
+
padding: var(--space-1) 10px;
|
|
58
|
+
border-radius: var(--radius-sm);
|
|
59
|
+
color: var(--sidebar-fg-muted);
|
|
60
|
+
font-size: 13.5px;
|
|
61
|
+
transition: background 0.12s ease;
|
|
62
|
+
}
|
|
63
|
+
.nav li a:hover { background: var(--sidebar-hover-bg); color: var(--sidebar-fg-strong); }
|
|
64
|
+
.nav li a.active { background: var(--sidebar-active-bg); color: var(--sidebar-fg-strong); }
|
|
65
|
+
.nav .nav-icon { display: inline-grid; place-items: center; width: 22px; height: 22px; flex: 0 0 22px; }
|
|
66
|
+
.nav .nav-icon svg {
|
|
67
|
+
width: 19px; height: 19px;
|
|
68
|
+
stroke: currentColor; fill: none;
|
|
69
|
+
stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.topbar {
|
|
73
|
+
background: var(--surface);
|
|
74
|
+
border-bottom: 1px solid var(--border);
|
|
75
|
+
padding: 10px var(--space-3);
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: var(--space-2);
|
|
79
|
+
position: sticky;
|
|
80
|
+
top: 0;
|
|
81
|
+
z-index: 10;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.content { padding: var(--space-3); max-width: 1280px; width: 100%; margin: 0 auto; }
|
|
85
|
+
.page-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 18px; gap: var(--space-2); }
|
|
86
|
+
.page-title { font-size: var(--fs-title); font-weight: var(--fw-semibold); margin: 0 0 var(--space-half); }
|
|
87
|
+
.page-sub { font-size: var(--fs-sm); color: var(--text-muted); margin: 0; }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Markup
|
|
91
|
+
|
|
92
|
+
**React** - data-driven nav.
|
|
93
|
+
```tsx
|
|
94
|
+
const NAV = [
|
|
95
|
+
{ label: 'Filing tasks', to: '/', icon: 'i-filing-tasks' },
|
|
96
|
+
{ label: 'Transactions', to: '/transactions', icon: 'i-transactions' },
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
export function Shell({ children }: { children: React.ReactNode }) {
|
|
100
|
+
return (
|
|
101
|
+
<div className="app">
|
|
102
|
+
<aside className="sidebar">
|
|
103
|
+
<div className="brand"><span className="brand-dot" />BevLogic</div>
|
|
104
|
+
<div className="nav-section">Returns</div>
|
|
105
|
+
<ul className="nav">
|
|
106
|
+
{NAV.map(n => (
|
|
107
|
+
<li key={n.to}>
|
|
108
|
+
<NavLink to={n.to} className={({ isActive }) => isActive ? 'active' : ''}>
|
|
109
|
+
<span className="nav-icon"><svg><use href={`/icons.svg#${n.icon}`} /></svg></span>
|
|
110
|
+
<span className="nav-label-text">{n.label}</span>
|
|
111
|
+
</NavLink>
|
|
112
|
+
</li>
|
|
113
|
+
))}
|
|
114
|
+
</ul>
|
|
115
|
+
</aside>
|
|
116
|
+
<div className="main">
|
|
117
|
+
<header className="topbar">{/* search, user menu */}</header>
|
|
118
|
+
<main className="content">{children}</main>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Static HTML**
|
|
126
|
+
```html
|
|
127
|
+
<div class="app">
|
|
128
|
+
<aside class="sidebar">
|
|
129
|
+
<div class="brand"><span class="brand-dot"></span>BevLogic</div>
|
|
130
|
+
<div class="nav-section">Rates</div>
|
|
131
|
+
<ul class="nav">
|
|
132
|
+
<li><a href="/rates" class="active">
|
|
133
|
+
<span class="nav-icon"><svg><use href="/icons.svg#i-rates" /></svg></span>
|
|
134
|
+
<span class="nav-label-text">Fixed rates</span>
|
|
135
|
+
</a></li>
|
|
136
|
+
</ul>
|
|
137
|
+
</aside>
|
|
138
|
+
<div class="main">
|
|
139
|
+
<header class="topbar"><!-- search / user --></header>
|
|
140
|
+
<main class="content">
|
|
141
|
+
<div class="page-header">
|
|
142
|
+
<div><h1 class="page-title">Fixed rates</h1><p class="page-sub">13 states</p></div>
|
|
143
|
+
</div>
|
|
144
|
+
<!-- page content -->
|
|
145
|
+
</main>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**PHP** - for a server-rendered app. Point the shell's tokens at these values
|
|
151
|
+
(navy `--sidebar-bg`, accent `--accent`) and render the nav from your own data.
|
|
152
|
+
```php
|
|
153
|
+
<div class="app">
|
|
154
|
+
<aside class="sidebar">
|
|
155
|
+
<div class="brand"><span class="brand-dot"></span>BevLogic</div>
|
|
156
|
+
<?php foreach ($sections as $section): ?>
|
|
157
|
+
<div class="nav-section"><?= htmlspecialchars($section['label']) ?></div>
|
|
158
|
+
<ul class="nav">
|
|
159
|
+
<?php foreach ($section['items'] as $item): ?>
|
|
160
|
+
<li><a href="<?= $item['href'] ?>" class="<?= $item['active'] ? 'active' : '' ?>">
|
|
161
|
+
<span class="nav-icon"><svg><use href="/icons.svg#<?= $item['icon'] ?>" /></svg></span>
|
|
162
|
+
<span class="nav-label-text"><?= htmlspecialchars($item['label']) ?></span>
|
|
163
|
+
</a></li>
|
|
164
|
+
<?php endforeach; ?>
|
|
165
|
+
</ul>
|
|
166
|
+
<?php endforeach; ?>
|
|
167
|
+
</aside>
|
|
168
|
+
<div class="main">
|
|
169
|
+
<header class="topbar"><!-- .bev-page-header equivalent --></header>
|
|
170
|
+
<main class="content"><?= $body ?></main>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Notes
|
|
176
|
+
- Sidebar keeps its own `--sidebar-*` tokens so it stays dark regardless of body theme.
|
|
177
|
+
- Collapsed mode (`.app.sidebar-collapsed`, 64px icon-only rail with hover tooltips) can be layered on; not reproduced here to keep the recipe focused.
|
|
178
|
+
- If you are only reskinning the shell, page bodies inside `.content` can keep their existing markup; align the shell first, content later.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Recipe: Badge & Chip
|
|
2
|
+
|
|
3
|
+
`.badge` — a status pill with a leading colored dot. `.chip` — a small
|
|
4
|
+
labeled token. Requires `tokens.css`.
|
|
5
|
+
|
|
6
|
+
## CSS (shared)
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
.badge {
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
gap: var(--space-half);
|
|
13
|
+
padding: 2px var(--space-1);
|
|
14
|
+
border-radius: var(--radius-pill);
|
|
15
|
+
font-size: var(--fs-label);
|
|
16
|
+
font-weight: var(--fw-medium);
|
|
17
|
+
background: var(--primary-soft);
|
|
18
|
+
color: var(--text);
|
|
19
|
+
}
|
|
20
|
+
.badge::before {
|
|
21
|
+
content: '';
|
|
22
|
+
width: 6px; height: 6px;
|
|
23
|
+
border-radius: 50%;
|
|
24
|
+
background: var(--text-faint);
|
|
25
|
+
}
|
|
26
|
+
.badge.success { background: var(--success-soft); color: var(--success); }
|
|
27
|
+
.badge.success::before { background: var(--success); }
|
|
28
|
+
.badge.warning { background: var(--warning-soft); color: var(--warning); }
|
|
29
|
+
.badge.warning::before { background: var(--warning); }
|
|
30
|
+
.badge.danger { background: var(--danger-soft); color: var(--danger); }
|
|
31
|
+
.badge.danger::before { background: var(--danger); }
|
|
32
|
+
.badge.info { background: var(--info-soft); color: var(--info); }
|
|
33
|
+
.badge.info::before { background: var(--info); }
|
|
34
|
+
.badge.accent { background: var(--accent-soft); color: var(--accent); }
|
|
35
|
+
.badge.accent::before { background: var(--accent); }
|
|
36
|
+
.badge.plain::before { display: none; }
|
|
37
|
+
|
|
38
|
+
.chip {
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: var(--space-half);
|
|
42
|
+
padding: 2px var(--space-1) 2px var(--space-half);
|
|
43
|
+
border-radius: var(--radius-pill);
|
|
44
|
+
background: var(--surface-2);
|
|
45
|
+
border: 1px solid var(--border);
|
|
46
|
+
font-size: var(--fs-meta);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Markup
|
|
51
|
+
|
|
52
|
+
**React**
|
|
53
|
+
```tsx
|
|
54
|
+
type BadgeTone = 'success' | 'warning' | 'danger' | 'info' | 'accent' | 'plain';
|
|
55
|
+
|
|
56
|
+
export function Badge({ tone, children }: { tone?: BadgeTone; children: React.ReactNode }) {
|
|
57
|
+
return <span className={`badge ${tone ?? ''}`}>{children}</span>;
|
|
58
|
+
}
|
|
59
|
+
// <Badge tone="success">Filed</Badge>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Static HTML**
|
|
63
|
+
```html
|
|
64
|
+
<span class="badge success">Filed</span>
|
|
65
|
+
<span class="badge warning">Due soon</span>
|
|
66
|
+
<span class="badge danger">Failed</span>
|
|
67
|
+
<span class="chip">CA · Sales</span>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**PHP**
|
|
71
|
+
```php
|
|
72
|
+
<?php function bev_badge(string $label, string $tone = ''): string {
|
|
73
|
+
return '<span class="badge ' . htmlspecialchars($tone) . '">' . htmlspecialchars($label) . '</span>';
|
|
74
|
+
} ?>
|
|
75
|
+
<?= bev_badge('Filed', 'success') ?>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Notes
|
|
79
|
+
- Tone maps to meaning: success/warning/danger/info per the status tokens. Don't pick a tone for looks.
|
|
80
|
+
- `.plain` drops the dot for non-status labels.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Recipe: Button
|
|
2
|
+
|
|
3
|
+
`.btn` with `primary` / `ghost` / `sm` modifiers. Requires `tokens.css`.
|
|
4
|
+
|
|
5
|
+
## CSS (shared — token-based, identical across stacks)
|
|
6
|
+
|
|
7
|
+
```css
|
|
8
|
+
.btn {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: var(--space-half);
|
|
12
|
+
padding: 7px 13px;
|
|
13
|
+
border-radius: var(--radius-sm);
|
|
14
|
+
border: 1px solid var(--border-strong);
|
|
15
|
+
background: var(--surface);
|
|
16
|
+
font: inherit;
|
|
17
|
+
font-size: var(--fs-sm);
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
color: var(--text);
|
|
20
|
+
transition: background 0.12s ease, border-color 0.12s ease;
|
|
21
|
+
}
|
|
22
|
+
.btn:hover { background: var(--surface-2); border-color: var(--text-faint); }
|
|
23
|
+
|
|
24
|
+
.btn.primary { background: var(--accent); color: var(--text-on-accent); border-color: var(--accent); }
|
|
25
|
+
.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
|
26
|
+
|
|
27
|
+
/* Gradient accent: the single headline action on a screen (hero, onboarding,
|
|
28
|
+
empty state). The flat .primary stays the default for ordinary views. */
|
|
29
|
+
.btn.accent { background: var(--grad-accent); color: var(--text-on-accent); border-color: transparent; }
|
|
30
|
+
.btn.accent:hover { background: var(--grad-accent-deep); border-color: transparent; }
|
|
31
|
+
|
|
32
|
+
.btn.ghost { background: transparent; border-color: transparent; color: var(--text-muted); }
|
|
33
|
+
.btn.ghost:hover { background: var(--primary-soft); color: var(--text); }
|
|
34
|
+
|
|
35
|
+
.btn.sm { padding: var(--space-half) 9px; font-size: var(--fs-meta); }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Markup
|
|
39
|
+
|
|
40
|
+
**React**
|
|
41
|
+
```tsx
|
|
42
|
+
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
43
|
+
variant?: 'default' | 'primary' | 'accent' | 'ghost';
|
|
44
|
+
size?: 'default' | 'sm';
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function Button({ variant = 'default', size = 'default', className = '', ...props }: ButtonProps) {
|
|
48
|
+
const cls = ['btn', variant !== 'default' && variant, size === 'sm' && 'sm', className]
|
|
49
|
+
.filter(Boolean).join(' ');
|
|
50
|
+
return <button className={cls} {...props} />;
|
|
51
|
+
}
|
|
52
|
+
// <Button variant="primary">New return</Button>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Static HTML**
|
|
56
|
+
```html
|
|
57
|
+
<button class="btn primary">New return</button>
|
|
58
|
+
<button class="btn accent">Start filing</button>
|
|
59
|
+
<button class="btn">Cancel</button>
|
|
60
|
+
<button class="btn ghost sm">Dismiss</button>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**PHP**
|
|
64
|
+
```php
|
|
65
|
+
<?php function bev_button(string $label, string $variant = '', string $size = ''): string {
|
|
66
|
+
$cls = trim('btn ' . $variant . ' ' . $size);
|
|
67
|
+
return '<button class="' . htmlspecialchars($cls) . '">' . htmlspecialchars($label) . '</button>';
|
|
68
|
+
} ?>
|
|
69
|
+
<?= bev_button('New return', 'primary') ?>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## When to use which
|
|
73
|
+
|
|
74
|
+
- **Accent** (gradient) = the single headline action on a screen. The one thing
|
|
75
|
+
you want the user to do next: onboarding, empty state, "start a filing". One
|
|
76
|
+
per screen, so it stays the obvious next step.
|
|
77
|
+
- **Primary** (solid accent) = the main action in ordinary views. One per view:
|
|
78
|
+
toolbars, forms, dialog confirm. The default for the important action when
|
|
79
|
+
there's no headline moment.
|
|
80
|
+
- **Default** = secondary actions beside a primary. Cancel, back, "save draft".
|
|
81
|
+
Neutral, doesn't compete with the main action.
|
|
82
|
+
- **Ghost** = low-emphasis actions in dense UI. Table row actions, dismiss,
|
|
83
|
+
toolbar overflow. Quiet by design.
|
|
84
|
+
- **Small** (`sm`) = a size modifier, not a variant. Combine with any of the above
|
|
85
|
+
(`btn primary sm`) for tight rows: table cells, inline toolbars.
|
|
86
|
+
|
|
87
|
+
Rule of thumb per view: at most one accent OR one primary as the leading action,
|
|
88
|
+
never both competing; default and ghost carry everything else.
|
package/recipes/card.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Recipe: Card & Stat tile
|
|
2
|
+
|
|
3
|
+
`.card` (surface container) + `.card-h` (header row), and the `.stat` KPI tile.
|
|
4
|
+
Requires `tokens.css`.
|
|
5
|
+
|
|
6
|
+
## CSS (shared)
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
.card {
|
|
10
|
+
background: var(--surface);
|
|
11
|
+
border: 1px solid var(--border);
|
|
12
|
+
border-radius: var(--radius);
|
|
13
|
+
padding: var(--space-2);
|
|
14
|
+
box-shadow: var(--shadow-sm);
|
|
15
|
+
}
|
|
16
|
+
.card-h {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
margin-bottom: 12px;
|
|
21
|
+
}
|
|
22
|
+
.card-h h3 { font-size: var(--fs-h); font-weight: var(--fw-semibold); margin: 0; color: var(--text); }
|
|
23
|
+
.card-h .muted { font-size: var(--fs-meta); color: var(--text-muted); }
|
|
24
|
+
|
|
25
|
+
/* Stat / KPI tile */
|
|
26
|
+
.stat {
|
|
27
|
+
background: var(--surface);
|
|
28
|
+
border: 1px solid var(--border);
|
|
29
|
+
border-radius: var(--radius);
|
|
30
|
+
padding: 14px var(--space-2);
|
|
31
|
+
}
|
|
32
|
+
.stat-label {
|
|
33
|
+
font-size: var(--fs-label);
|
|
34
|
+
text-transform: uppercase;
|
|
35
|
+
letter-spacing: 0.6px;
|
|
36
|
+
color: var(--text-muted);
|
|
37
|
+
margin-bottom: var(--space-half);
|
|
38
|
+
}
|
|
39
|
+
.stat-value { font-size: var(--fs-stat); font-weight: var(--fw-semibold); letter-spacing: -0.2px; }
|
|
40
|
+
.stat-delta { font-size: var(--fs-label); margin-top: var(--space-half); color: var(--text-muted); }
|
|
41
|
+
.stat-delta.up { color: var(--success); }
|
|
42
|
+
.stat-delta.down { color: var(--danger); }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Markup
|
|
46
|
+
|
|
47
|
+
**React**
|
|
48
|
+
```tsx
|
|
49
|
+
export function Card({ title, action, children }: {
|
|
50
|
+
title?: string; action?: React.ReactNode; children: React.ReactNode;
|
|
51
|
+
}) {
|
|
52
|
+
return (
|
|
53
|
+
<div className="card">
|
|
54
|
+
{title && <div className="card-h"><h3>{title}</h3>{action}</div>}
|
|
55
|
+
{children}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function Stat({ label, value, delta, dir }: {
|
|
61
|
+
label: string; value: string; delta?: string; dir?: 'up' | 'down';
|
|
62
|
+
}) {
|
|
63
|
+
return (
|
|
64
|
+
<div className="stat">
|
|
65
|
+
<div className="stat-label">{label}</div>
|
|
66
|
+
<div className="stat-value">{value}</div>
|
|
67
|
+
{delta && <div className={`stat-delta ${dir ?? ''}`}>{delta}</div>}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Static HTML**
|
|
74
|
+
```html
|
|
75
|
+
<div class="card">
|
|
76
|
+
<div class="card-h"><h3>Filing tasks</h3><span class="muted">This week</span></div>
|
|
77
|
+
<!-- content -->
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="stat">
|
|
81
|
+
<div class="stat-label">Open returns</div>
|
|
82
|
+
<div class="stat-value">42</div>
|
|
83
|
+
<div class="stat-delta up">+6 vs last week</div>
|
|
84
|
+
</div>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**PHP**
|
|
88
|
+
```php
|
|
89
|
+
<div class="card">
|
|
90
|
+
<div class="card-h"><h3><?= htmlspecialchars($title) ?></h3></div>
|
|
91
|
+
<?= $body /* trusted markup */ ?>
|
|
92
|
+
</div>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Notes
|
|
96
|
+
- Group stat tiles in a 4-up grid that collapses to 2-up under 1000px.
|
|
97
|
+
- Card padding is `--space-2` (16px) for new work; older screens use 18px — fine to leave, don't churn.
|
package/recipes/input.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Recipe: Input & Search field
|
|
2
|
+
|
|
3
|
+
A text input primitive plus the topbar `.search` field. Requires `tokens.css`.
|
|
4
|
+
|
|
5
|
+
> This `.input` primitive codifies a consistent standalone field for new work,
|
|
6
|
+
> alongside the topbar `.search` container.
|
|
7
|
+
|
|
8
|
+
## CSS (shared)
|
|
9
|
+
|
|
10
|
+
```css
|
|
11
|
+
.input {
|
|
12
|
+
width: 100%;
|
|
13
|
+
padding: 7px 12px;
|
|
14
|
+
font: inherit;
|
|
15
|
+
font-size: var(--fs-sm);
|
|
16
|
+
color: var(--text);
|
|
17
|
+
background: var(--surface);
|
|
18
|
+
border: 1px solid var(--border-strong);
|
|
19
|
+
border-radius: var(--radius-sm);
|
|
20
|
+
outline: none;
|
|
21
|
+
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
22
|
+
}
|
|
23
|
+
.input::placeholder { color: var(--text-faint); }
|
|
24
|
+
.input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
|
|
25
|
+
.input:disabled { background: var(--surface-2); color: var(--text-faint); cursor: not-allowed; }
|
|
26
|
+
|
|
27
|
+
/* Labeled field wrapper */
|
|
28
|
+
.field { display: flex; flex-direction: column; gap: var(--space-half); }
|
|
29
|
+
.field label { font-size: var(--fs-meta); color: var(--text-muted); font-weight: var(--fw-medium); }
|
|
30
|
+
.field-error { font-size: var(--fs-meta); color: var(--danger); }
|
|
31
|
+
|
|
32
|
+
/* Topbar search (icon + borderless input in a filled container) */
|
|
33
|
+
.search {
|
|
34
|
+
flex: 1;
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: var(--space-1);
|
|
38
|
+
background: var(--surface-2);
|
|
39
|
+
border: 1px solid var(--border);
|
|
40
|
+
border-radius: var(--radius);
|
|
41
|
+
padding: 7px 12px;
|
|
42
|
+
max-width: 520px;
|
|
43
|
+
}
|
|
44
|
+
.search input { flex: 1; border: none; background: transparent; outline: none; font: inherit; color: var(--text); }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Markup
|
|
48
|
+
|
|
49
|
+
**React**
|
|
50
|
+
```tsx
|
|
51
|
+
export function Field({ label, error, ...props }: {
|
|
52
|
+
label?: string; error?: string;
|
|
53
|
+
} & React.InputHTMLAttributes<HTMLInputElement>) {
|
|
54
|
+
return (
|
|
55
|
+
<div className="field">
|
|
56
|
+
{label && <label>{label}</label>}
|
|
57
|
+
<input className="input" {...props} />
|
|
58
|
+
{error && <span className="field-error">{error}</span>}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Static HTML**
|
|
65
|
+
```html
|
|
66
|
+
<div class="field">
|
|
67
|
+
<label for="ein">EIN</label>
|
|
68
|
+
<input class="input" id="ein" type="text" placeholder="00-0000000" />
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="search">
|
|
72
|
+
<svg class="icon" aria-hidden="true"><use href="/icons.svg#i-search" /></svg>
|
|
73
|
+
<input type="search" placeholder="Search…" />
|
|
74
|
+
<span class="kbd">⌘K</span>
|
|
75
|
+
</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**PHP**
|
|
79
|
+
```php
|
|
80
|
+
<div class="field">
|
|
81
|
+
<label for="<?= $id ?>"><?= htmlspecialchars($label) ?></label>
|
|
82
|
+
<input class="input" id="<?= $id ?>" name="<?= $id ?>" value="<?= htmlspecialchars($value) ?>" />
|
|
83
|
+
</div>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Notes
|
|
87
|
+
- Focus ring is `--accent` border + `--focus-ring` glow, consistent across all inputs.
|
package/recipes/table.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Recipe: Data table
|
|
2
|
+
|
|
3
|
+
`table.data` — the dense data table with uppercase headers and hover rows.
|
|
4
|
+
Requires `tokens.css`.
|
|
5
|
+
|
|
6
|
+
## CSS (shared)
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
table.data {
|
|
10
|
+
width: 100%;
|
|
11
|
+
border-collapse: collapse;
|
|
12
|
+
font-size: var(--fs-sm);
|
|
13
|
+
}
|
|
14
|
+
table.data th {
|
|
15
|
+
text-align: left;
|
|
16
|
+
padding: 9px 12px;
|
|
17
|
+
font-weight: var(--fw-semibold);
|
|
18
|
+
font-size: var(--fs-label);
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
letter-spacing: 0.5px;
|
|
21
|
+
color: var(--text-muted);
|
|
22
|
+
background: var(--surface-2);
|
|
23
|
+
border-bottom: 1px solid var(--border);
|
|
24
|
+
}
|
|
25
|
+
table.data td {
|
|
26
|
+
padding: 10px 12px;
|
|
27
|
+
border-bottom: 1px solid var(--border);
|
|
28
|
+
vertical-align: middle;
|
|
29
|
+
}
|
|
30
|
+
table.data tr:last-child td { border-bottom: none; }
|
|
31
|
+
table.data tr:hover td { background: var(--surface-2); }
|
|
32
|
+
table.data .row-link td:first-child { font-weight: var(--fw-medium); }
|
|
33
|
+
|
|
34
|
+
/* Right-align + tabular figures for numeric columns */
|
|
35
|
+
table.data td.numeric,
|
|
36
|
+
table.data th.numeric { text-align: right; font-variant-numeric: tabular-nums; }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Markup
|
|
40
|
+
|
|
41
|
+
**React**
|
|
42
|
+
```tsx
|
|
43
|
+
export function DataTable<T>({ columns, rows, keyOf }: {
|
|
44
|
+
columns: { key: keyof T; label: string; numeric?: boolean }[];
|
|
45
|
+
rows: T[];
|
|
46
|
+
keyOf: (row: T) => string;
|
|
47
|
+
}) {
|
|
48
|
+
return (
|
|
49
|
+
<table className="data">
|
|
50
|
+
<thead><tr>
|
|
51
|
+
{columns.map(c => (
|
|
52
|
+
<th key={String(c.key)} className={c.numeric ? 'numeric' : ''}>{c.label}</th>
|
|
53
|
+
))}
|
|
54
|
+
</tr></thead>
|
|
55
|
+
<tbody>
|
|
56
|
+
{rows.map(row => (
|
|
57
|
+
<tr key={keyOf(row)}>
|
|
58
|
+
{columns.map(c => (
|
|
59
|
+
<td key={String(c.key)} className={c.numeric ? 'numeric' : ''}>{String(row[c.key])}</td>
|
|
60
|
+
))}
|
|
61
|
+
</tr>
|
|
62
|
+
))}
|
|
63
|
+
</tbody>
|
|
64
|
+
</table>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Static HTML**
|
|
70
|
+
```html
|
|
71
|
+
<table class="data">
|
|
72
|
+
<thead>
|
|
73
|
+
<tr><th>Jurisdiction</th><th>Period</th><th class="numeric">Liability</th><th>Status</th></tr>
|
|
74
|
+
</thead>
|
|
75
|
+
<tbody>
|
|
76
|
+
<tr class="row-link">
|
|
77
|
+
<td>California</td><td>2026-Q2</td><td class="numeric">$12,480.00</td>
|
|
78
|
+
<td><span class="badge success">Filed</span></td>
|
|
79
|
+
</tr>
|
|
80
|
+
</tbody>
|
|
81
|
+
</table>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**PHP**
|
|
85
|
+
```php
|
|
86
|
+
<table class="data">
|
|
87
|
+
<thead><tr><?php foreach ($cols as $c): ?>
|
|
88
|
+
<th class="<?= !empty($c['numeric']) ? 'numeric' : '' ?>"><?= htmlspecialchars($c['label']) ?></th>
|
|
89
|
+
<?php endforeach; ?></tr></thead>
|
|
90
|
+
<tbody><?php foreach ($rows as $r): ?>
|
|
91
|
+
<tr><?php foreach ($cols as $c): ?>
|
|
92
|
+
<td class="<?= !empty($c['numeric']) ? 'numeric' : '' ?>"><?= htmlspecialchars($r[$c['key']]) ?></td>
|
|
93
|
+
<?php endforeach; ?></tr>
|
|
94
|
+
<?php endforeach; ?></tbody>
|
|
95
|
+
</table>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Notes
|
|
99
|
+
- Money/count columns get `.numeric` (right-aligned, tabular figures) so digits line up.
|
|
100
|
+
- Status cells use a `.badge` (see `recipes/badge-pill.md`), not colored text.
|
package/tokens.css
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @bevlogic/style-system — tokens.css
|
|
3
|
+
*
|
|
4
|
+
* The single source of truth for BevLogic UI design tokens.
|
|
5
|
+
*
|
|
6
|
+
* Consume this file, don't fork its values. If you need a value that isn't
|
|
7
|
+
* here, that's a signal to add a token, not to hardcode a hex/px in your app.
|
|
8
|
+
*
|
|
9
|
+
* Light theme only for now; dark-mode tokens are parked until dark mode is
|
|
10
|
+
* formally on the roadmap.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
:root {
|
|
14
|
+
color-scheme: light;
|
|
15
|
+
|
|
16
|
+
/* ---- Layout rails ---- */
|
|
17
|
+
--sidebar-width: 240px;
|
|
18
|
+
--sidebar-collapsed-width: 64px;
|
|
19
|
+
|
|
20
|
+
/* ---- Surfaces & neutrals ---- */
|
|
21
|
+
--bg: #f5f6f8;
|
|
22
|
+
--surface: #ffffff;
|
|
23
|
+
--surface-2: #fafbfc;
|
|
24
|
+
--surface-raised: #ffffff;
|
|
25
|
+
--border: #e4e7eb;
|
|
26
|
+
--border-strong: #cbd2d9;
|
|
27
|
+
|
|
28
|
+
/* ---- Text ---- */
|
|
29
|
+
--text: #1f2933;
|
|
30
|
+
--text-muted: #616e7c;
|
|
31
|
+
--text-faint: #9aa5b1;
|
|
32
|
+
--text-on-accent: #ffffff;
|
|
33
|
+
|
|
34
|
+
/* ---- Brand ---- */
|
|
35
|
+
--primary: #2d3a4f; /* BevLogic navy */
|
|
36
|
+
--primary-deep: #232e40; /* darker navy: gradient depth (was inline in the hero) */
|
|
37
|
+
--primary-deeper: #1f2933; /* deepest navy: hero/panel floor */
|
|
38
|
+
--primary-soft: #eef1f6;
|
|
39
|
+
--accent: #5a6cff; /* indigo/periwinkle: the interactive accent */
|
|
40
|
+
--accent-soft: #ecefff;
|
|
41
|
+
--accent-hover: #4456e6;
|
|
42
|
+
--accent-gradient-2: #8b9bff;
|
|
43
|
+
|
|
44
|
+
/* Brand mark: the BevLogic sunburst, as a CSS mask value. Paint it by
|
|
45
|
+
setting a color and this mask, e.g. .brand-dot { background: var(--accent);
|
|
46
|
+
mask: var(--brand-mark) center/contain no-repeat; }. Source: brand-mark.svg. */
|
|
47
|
+
--brand-mark: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%20fill%3D%22%23000%22%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%280%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%2818%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%2836%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%2854%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%2872%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%2890%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28108%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28126%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28144%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28162%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28180%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28198%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28216%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28234%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28252%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28270%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28288%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28306%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28324%2050%2050%29%22%2F%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2217%22%20rx%3D%223.2%22%20ry%3D%227%22%20transform%3D%22rotate%28342%2050%2050%29%22%2F%3E%3C%2Fsvg%3E");
|
|
48
|
+
|
|
49
|
+
/* ---- Gradients (derived from the palette above; consume by name, don't
|
|
50
|
+
re-roll a linear-gradient() inline). Kept restrained on purpose: navy
|
|
51
|
+
family for depth, indigo reserved for small accent surfaces. ---- */
|
|
52
|
+
|
|
53
|
+
/* Sidebar rail: navy base with a faint indigo bloom top-left. */
|
|
54
|
+
--grad-sidebar:
|
|
55
|
+
radial-gradient(120% 60% at 20% 0%, rgba(90, 108, 255, 0.20), transparent 60%),
|
|
56
|
+
linear-gradient(180deg, var(--primary), #202a3a);
|
|
57
|
+
/* Sidebar right edge, as a border-image: indigo fading to nothing downward.
|
|
58
|
+
Use with: border-right: 2px solid transparent; border-image: var(--grad-sidebar-edge) 1; */
|
|
59
|
+
--grad-sidebar-edge:
|
|
60
|
+
linear-gradient(180deg, var(--accent-gradient-2), var(--accent) 45%, transparent);
|
|
61
|
+
|
|
62
|
+
/* Brand accent: small pop surfaces only (marks, active accents, progress, buttons). */
|
|
63
|
+
--grad-accent: linear-gradient(135deg, var(--accent), var(--accent-gradient-2));
|
|
64
|
+
--grad-accent-deep: linear-gradient(135deg, var(--accent-hover), var(--accent));
|
|
65
|
+
--grad-brand-bridge: linear-gradient(135deg, var(--primary), var(--accent));
|
|
66
|
+
|
|
67
|
+
/* Surface washes: barely-there panel/page backgrounds. */
|
|
68
|
+
--grad-surface: linear-gradient(180deg, var(--surface), var(--bg));
|
|
69
|
+
--grad-surface-cool: linear-gradient(160deg, var(--surface-2), var(--primary-soft));
|
|
70
|
+
--grad-accent-haze: radial-gradient(100% 90% at 100% 0%, var(--accent-soft), transparent 55%);
|
|
71
|
+
|
|
72
|
+
/* Hero / feature panels: dark, same navy family as the sidebar. */
|
|
73
|
+
--grad-hero:
|
|
74
|
+
radial-gradient(80% 120% at 100% 0%, rgba(139, 155, 255, 0.28), transparent 50%),
|
|
75
|
+
radial-gradient(70% 110% at 0% 100%, rgba(90, 108, 255, 0.16), transparent 55%),
|
|
76
|
+
linear-gradient(135deg, var(--primary), var(--primary-deeper));
|
|
77
|
+
--grad-hero-spotlight:
|
|
78
|
+
radial-gradient(120% 80% at 50% -20%, rgba(90, 108, 255, 0.30), transparent 60%),
|
|
79
|
+
var(--primary-deep);
|
|
80
|
+
|
|
81
|
+
/* Status soft tints: optional dimension for banner/callout cards. */
|
|
82
|
+
--grad-status-success: linear-gradient(180deg, #eafaf3, var(--success-soft));
|
|
83
|
+
--grad-status-warning: linear-gradient(180deg, #fff7e6, var(--warning-soft));
|
|
84
|
+
--grad-status-danger: linear-gradient(180deg, #fdecec, var(--danger-soft));
|
|
85
|
+
--grad-status-info: linear-gradient(180deg, #eef5ff, var(--info-soft));
|
|
86
|
+
|
|
87
|
+
/* ---- Semantic status (each has a -soft background tint) ---- */
|
|
88
|
+
--success: #137752; --success-soft: #def7ec;
|
|
89
|
+
--warning: #b54708; --warning-soft: #fef3c7;
|
|
90
|
+
--danger: #9b1c1c; --danger-soft: #fde2e2;
|
|
91
|
+
--info: #1c64f2; --info-soft: #e1effe;
|
|
92
|
+
|
|
93
|
+
/* ---- Sidebar (own tokens: stays visually dark regardless of body theme) ---- */
|
|
94
|
+
--sidebar-bg: #2d3a4f;
|
|
95
|
+
--sidebar-fg: #e4e7eb;
|
|
96
|
+
--sidebar-fg-muted: #cbd2d9;
|
|
97
|
+
--sidebar-fg-faint: #9aa5b1;
|
|
98
|
+
--sidebar-fg-strong: #ffffff;
|
|
99
|
+
--sidebar-hover-bg: rgba(255, 255, 255, 0.06);
|
|
100
|
+
--sidebar-active-bg: rgba(255, 255, 255, 0.1);
|
|
101
|
+
--sidebar-border: rgba(255, 255, 255, 0.15);
|
|
102
|
+
--sidebar-border-soft: rgba(255, 255, 255, 0.08);
|
|
103
|
+
--sidebar-divider: rgba(255, 255, 255, 0.12);
|
|
104
|
+
--sidebar-badge-muted-bg: rgba(255, 255, 255, 0.18);
|
|
105
|
+
--sidebar-tooltip-bg: #1f2933;
|
|
106
|
+
--sidebar-tooltip-fg: #ffffff;
|
|
107
|
+
|
|
108
|
+
/* ---- Overlays / focus ---- */
|
|
109
|
+
--backdrop: rgba(20, 30, 50, 0.32);
|
|
110
|
+
--backdrop-soft: rgba(20, 30, 50, 0.28);
|
|
111
|
+
--backdrop-strong: rgba(20, 30, 50, 0.4);
|
|
112
|
+
--focus-ring: rgba(90, 108, 255, 0.35);
|
|
113
|
+
--client-body-bg: #f9fafb;
|
|
114
|
+
|
|
115
|
+
/* ---- Radii ---- */
|
|
116
|
+
--radius: 8px;
|
|
117
|
+
--radius-sm: 4px;
|
|
118
|
+
--radius-pill: 999px;
|
|
119
|
+
|
|
120
|
+
/* ---- Shadows ---- */
|
|
121
|
+
--shadow-sm: 0 1px 2px rgba(20, 30, 50, 0.05);
|
|
122
|
+
--shadow: 0 4px 14px rgba(20, 30, 50, 0.08);
|
|
123
|
+
--shadow-lg: 0 12px 28px rgba(20, 30, 50, 0.14);
|
|
124
|
+
--shadow-tooltip: 0 8px 22px rgba(0, 0, 0, 0.24);
|
|
125
|
+
|
|
126
|
+
/* ---- Fonts ---- */
|
|
127
|
+
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
|
|
128
|
+
--mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* ===================================================================
|
|
132
|
+
* ADDITIONS — codified here, not present in the source apps yet.
|
|
133
|
+
* The apps hardcode these values inline; the style guide formalizes
|
|
134
|
+
* them so new work has named tokens to reach for.
|
|
135
|
+
* ===================================================================
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
/* ---- Spacing scale (8px grid — prefer multiples of 8) ----
|
|
139
|
+
--space-half is a deliberate escape hatch for genuinely tight cases
|
|
140
|
+
(icon gaps, hairline insets). It is NOT a normal step — reach for the
|
|
141
|
+
8-based steps first. */
|
|
142
|
+
--space-half: 4px;
|
|
143
|
+
--space-1: 8px;
|
|
144
|
+
--space-2: 16px;
|
|
145
|
+
--space-3: 24px;
|
|
146
|
+
--space-4: 32px;
|
|
147
|
+
--space-5: 48px;
|
|
148
|
+
--space-6: 64px;
|
|
149
|
+
|
|
150
|
+
/* ---- Type scale (the de-facto sizes across the apps, now named) ---- */
|
|
151
|
+
--fs-title: 22px; /* page titles, detail headers */
|
|
152
|
+
--fs-stat: 24px; /* KPI / stat values */
|
|
153
|
+
--fs-h: 14px; /* card / section headings */
|
|
154
|
+
--fs-body: 14px; /* base body */
|
|
155
|
+
--fs-sm: 13px; /* dense tables, secondary body */
|
|
156
|
+
--fs-meta: 12.5px; /* meta, captions */
|
|
157
|
+
--fs-label: 11.5px; /* uppercase eyebrow labels */
|
|
158
|
+
|
|
159
|
+
/* ---- Font weights ---- */
|
|
160
|
+
--fw-regular: 400;
|
|
161
|
+
--fw-medium: 500;
|
|
162
|
+
--fw-semibold: 600;
|
|
163
|
+
--fw-bold: 700;
|
|
164
|
+
|
|
165
|
+
/* ---- Line height ---- */
|
|
166
|
+
--lh-base: 1.5;
|
|
167
|
+
}
|