@birdycom/omniaui 0.0.0-stage → 0.11.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/LICENSE +21 -0
- package/README.md +20 -2
- package/bin/lib/add.mjs +126 -0
- package/bin/lib/catalog.mjs +169 -0
- package/bin/lib/project.mjs +90 -0
- package/bin/omniaui.mjs +70 -0
- package/dist/code-block.css +163 -0
- package/dist/components-extra.css +914 -0
- package/dist/components.css +10081 -0
- package/dist/components.min.css +1 -0
- package/dist/data-table.d.ts +9 -0
- package/dist/data-table.js +505 -0
- package/dist/docs-ui.css +265 -0
- package/dist/oc-theme.css +69 -0
- package/dist/parts/.gitkeep +0 -0
- package/dist/parts/app-accordion-accent.css +60 -0
- package/dist/parts/app-alpha-tabs.css +68 -0
- package/dist/parts/app-busy-overlay.css +25 -0
- package/dist/parts/app-column-menu.css +85 -0
- package/dist/parts/app-confirm-dialog.css +70 -0
- package/dist/parts/app-cookie-consent.css +63 -0
- package/dist/parts/app-data-table.css +174 -0
- package/dist/parts/app-greeting.css +47 -0
- package/dist/parts/app-list-toolbar.css +81 -0
- package/dist/parts/app-page-header.css +54 -0
- package/dist/parts/app-severity-card.css +91 -0
- package/dist/parts/app-sidebar-dark.css +15 -0
- package/dist/parts/app-sortable-row.css +52 -0
- package/dist/parts/app-stat-card.css +68 -0
- package/dist/parts/app-table-actions.css +71 -0
- package/dist/parts/app-table-compact.css +54 -0
- package/dist/parts/app-top-progress.css +68 -0
- package/dist/parts/app-tree.css +104 -0
- package/dist/parts/app-upgrade-banner.css +90 -0
- package/dist/parts/app-version-banner.css +80 -0
- package/dist/parts/data-chart-line.css +167 -0
- package/dist/parts/forms-affix-input.css +75 -0
- package/dist/parts/forms-barcode-input.css +75 -0
- package/dist/parts/forms-chip-filter.css +91 -0
- package/dist/parts/forms-color-select.css +89 -0
- package/dist/parts/forms-decimal-input.css +51 -0
- package/dist/parts/forms-filter-counts.css +94 -0
- package/dist/parts/forms-language-select.css +76 -0
- package/dist/parts/forms-number-input.css +95 -0
- package/dist/parts/forms-password-requirements.css +55 -0
- package/dist/parts/forms-phone-input.css +85 -0
- package/dist/parts/forms-time-input.css +74 -0
- package/dist/parts/forms-toggle-field.css +69 -0
- package/dist/parts/media-art.css +21 -0
- package/dist/parts/media-attachment.css +160 -0
- package/dist/parts/media-browser-frame.css +73 -0
- package/dist/parts/media-card.css +83 -0
- package/dist/parts/media-chat-composer.css +90 -0
- package/dist/parts/media-doc-viewer.css +151 -0
- package/dist/parts/media-emoji-picker.css +88 -0
- package/dist/parts/media-entity-card.css +52 -0
- package/dist/parts/media-flip-card.css +78 -0
- package/dist/parts/media-image-zoom.css +68 -0
- package/dist/parts/media-lightbox.css +131 -0
- package/dist/parts/media-message-thread.css +136 -0
- package/dist/parts/media-message.css +160 -0
- package/dist/parts/media-row.css +78 -0
- package/dist/parts/media-stack-card.css +82 -0
- package/dist/parts/media-terminal-log.css +76 -0
- package/dist/parts/media-thumb.css +71 -0
- package/dist/parts/site-fact-list.css +25 -0
- package/dist/parts/site-link-arrow.css +34 -0
- package/dist/parts/site-marquee.css +34 -0
- package/dist/parts/site-next-teaser.css +75 -0
- package/dist/parts/site-price-card.css +75 -0
- package/dist/parts/site-product-mark.css +66 -0
- package/dist/parts/site-project-card.css +111 -0
- package/dist/parts/site-prose.css +124 -0
- package/dist/parts/site-stack-list.css +48 -0
- package/dist/parts/site-timeline.css +54 -0
- package/dist/showcase.css +404 -0
- package/manifest.json +1108 -0
- package/package.json +67 -4
- package/snippets/accordion-accent.html +24 -0
- package/snippets/accordion.html +11 -0
- package/snippets/affix-input.html +30 -0
- package/snippets/alert-dialog.html +22 -0
- package/snippets/alert.html +5 -0
- package/snippets/alpha-tabs.html +37 -0
- package/snippets/aspect-ratio.html +15 -0
- package/snippets/attachment.html +43 -0
- package/snippets/avatar.html +6 -0
- package/snippets/back-link.html +5 -0
- package/snippets/badge.html +5 -0
- package/snippets/barcode-input.html +18 -0
- package/snippets/breadcrumb.html +6 -0
- package/snippets/browser-frame.html +18 -0
- package/snippets/busy-overlay.html +31 -0
- package/snippets/button-group.html +21 -0
- package/snippets/button.html +8 -0
- package/snippets/calendar.html +43 -0
- package/snippets/card.html +17 -0
- package/snippets/carousel.html +21 -0
- package/snippets/chart-line.html +46 -0
- package/snippets/chart.html +27 -0
- package/snippets/chat-composer.html +21 -0
- package/snippets/checkbox.html +5 -0
- package/snippets/checklist.html +23 -0
- package/snippets/chip-filter.html +14 -0
- package/snippets/code-window.html +20 -0
- package/snippets/collapsible.html +19 -0
- package/snippets/color-field.html +13 -0
- package/snippets/color-select.html +18 -0
- package/snippets/column-menu.html +38 -0
- package/snippets/combobox.html +19 -0
- package/snippets/command.html +24 -0
- package/snippets/confirm-dialog.html +32 -0
- package/snippets/context-menu.html +25 -0
- package/snippets/cookie-consent.html +70 -0
- package/snippets/copy-field.html +19 -0
- package/snippets/counter.html +2 -0
- package/snippets/data-table.html +151 -0
- package/snippets/date-picker.html +32 -0
- package/snippets/decimal-input.html +20 -0
- package/snippets/definition-list.html +20 -0
- package/snippets/demo-panel.html +18 -0
- package/snippets/doc-viewer.html +58 -0
- package/snippets/drawer.html +30 -0
- package/snippets/dropdown-menu.html +27 -0
- package/snippets/dropdown.html +9 -0
- package/snippets/dropzone.html +13 -0
- package/snippets/emoji-picker.html +51 -0
- package/snippets/empty-state.html +17 -0
- package/snippets/entity-card.html +26 -0
- package/snippets/expandable-table.html +26 -0
- package/snippets/fact-list.html +14 -0
- package/snippets/filter-counts.html +30 -0
- package/snippets/flip-card.html +22 -0
- package/snippets/form-field.html +6 -0
- package/snippets/greeting.html +22 -0
- package/snippets/heading.html +14 -0
- package/snippets/hover-card.html +18 -0
- package/snippets/image-zoom.html +30 -0
- package/snippets/input-group.html +10 -0
- package/snippets/input.html +5 -0
- package/snippets/item.html +26 -0
- package/snippets/kbd.html +13 -0
- package/snippets/label.html +9 -0
- package/snippets/language-select.html +26 -0
- package/snippets/launcher-card.html +14 -0
- package/snippets/lightbox.html +50 -0
- package/snippets/link-arrow.html +10 -0
- package/snippets/list-toolbar.html +33 -0
- package/snippets/marquee.html +15 -0
- package/snippets/media-card.html +28 -0
- package/snippets/media-row.html +21 -0
- package/snippets/media-thumb.html +12 -0
- package/snippets/menubar.html +30 -0
- package/snippets/message-thread.html +123 -0
- package/snippets/message.html +51 -0
- package/snippets/modal.html +15 -0
- package/snippets/native-select.html +15 -0
- package/snippets/navmenu.html +23 -0
- package/snippets/next-teaser.html +15 -0
- package/snippets/number-input.html +27 -0
- package/snippets/otp-input.html +9 -0
- package/snippets/page-header.html +24 -0
- package/snippets/pagination.html +6 -0
- package/snippets/password-input.html +24 -0
- package/snippets/password-requirements.html +30 -0
- package/snippets/phone-input.html +22 -0
- package/snippets/popover.html +15 -0
- package/snippets/price-card.html +34 -0
- package/snippets/product-mark.html +13 -0
- package/snippets/progress.html +4 -0
- package/snippets/project-card.html +23 -0
- package/snippets/prose.html +54 -0
- package/snippets/radio.html +17 -0
- package/snippets/recovery-codes.html +20 -0
- package/snippets/resizable.html +16 -0
- package/snippets/scroll-area.html +20 -0
- package/snippets/search.html +8 -0
- package/snippets/segmented.html +11 -0
- package/snippets/select.html +7 -0
- package/snippets/separator.html +15 -0
- package/snippets/severity-card.html +35 -0
- package/snippets/sheet.html +32 -0
- package/snippets/sidebar-docs.html +24 -0
- package/snippets/sidebar.html +55 -0
- package/snippets/site-footer.html +6 -0
- package/snippets/site-header.html +26 -0
- package/snippets/skeleton.html +4 -0
- package/snippets/slider.html +17 -0
- package/snippets/sortable-row.html +31 -0
- package/snippets/spinner.html +14 -0
- package/snippets/stack-card.html +17 -0
- package/snippets/stack-list.html +16 -0
- package/snippets/stat-card.html +24 -0
- package/snippets/stepper.html +13 -0
- package/snippets/switch.html +6 -0
- package/snippets/table-actions.html +39 -0
- package/snippets/table-compact.html +24 -0
- package/snippets/table.html +29 -0
- package/snippets/tabs.html +9 -0
- package/snippets/tag-input.html +17 -0
- package/snippets/terminal-log.html +23 -0
- package/snippets/textarea.html +3 -0
- package/snippets/theme-switch.html +6 -0
- package/snippets/time-input.html +26 -0
- package/snippets/timeline.html +28 -0
- package/snippets/toast-stack.html +10 -0
- package/snippets/toast.html +36 -0
- package/snippets/toc.html +13 -0
- package/snippets/toggle-field.html +20 -0
- package/snippets/toggle.html +22 -0
- package/snippets/tooltip.html +4 -0
- package/snippets/top-progress.html +12 -0
- package/snippets/tree.html +38 -0
- package/snippets/upgrade-banner.html +28 -0
- package/snippets/user.html +15 -0
- package/snippets/version-banner.html +15 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-table-actions.css | Map: icon actions -> tone modifiers -> overflow trigger -> menu tweaks -> reduced motion */
|
|
2
|
+
/* Table actions: row actions as small icon buttons or as one overflow ("more") menu.
|
|
3
|
+
Builds on the existing cell <td class="oc-table-actions"> from the table component.
|
|
4
|
+
The overflow menu is a native popover (.oc-dropdown-menu[popover]); no JavaScript needed. */
|
|
5
|
+
|
|
6
|
+
/* Icon mode: tighter gap between the square icon buttons. */
|
|
7
|
+
.oc-table-actions--icons > * + * { margin-inline-start: 0.125rem; }
|
|
8
|
+
|
|
9
|
+
/* One square icon action (link or button). Needs aria-label (and title for a tooltip). */
|
|
10
|
+
.oc-table-actions-icon {
|
|
11
|
+
display: inline-grid;
|
|
12
|
+
place-items: center;
|
|
13
|
+
inline-size: 1.75rem;
|
|
14
|
+
block-size: 1.75rem;
|
|
15
|
+
padding: 0;
|
|
16
|
+
font: inherit;
|
|
17
|
+
color: var(--oc-muted-foreground);
|
|
18
|
+
background: none;
|
|
19
|
+
border: 1px solid transparent;
|
|
20
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
text-decoration: none;
|
|
23
|
+
vertical-align: middle;
|
|
24
|
+
transition: color var(--oc-transition-fast), background var(--oc-transition-fast), border-color var(--oc-transition-fast);
|
|
25
|
+
}
|
|
26
|
+
.oc-table-actions-icon svg { inline-size: 1rem; block-size: 1rem; }
|
|
27
|
+
.oc-table-actions-icon:hover { color: var(--oc-foreground); background: var(--oc-accent); border-color: var(--oc-border); }
|
|
28
|
+
.oc-table-actions-icon:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
29
|
+
.oc-table-actions-icon:disabled,
|
|
30
|
+
.oc-table-actions-icon[aria-disabled="true"] { opacity: 0.45; pointer-events: none; }
|
|
31
|
+
|
|
32
|
+
/* Tone modifiers: the hover tint shows what the action does. */
|
|
33
|
+
.oc-table-actions-icon--primary:hover { color: var(--oc-primary); background: oklch(from var(--oc-primary) l c h / 0.1); }
|
|
34
|
+
.oc-table-actions-icon--success:hover { color: var(--oc-success); background: oklch(from var(--oc-success) l c h / 0.1); }
|
|
35
|
+
.oc-table-actions-icon--destructive { color: var(--oc-destructive); }
|
|
36
|
+
.oc-table-actions-icon--destructive:hover { color: var(--oc-destructive); background: oklch(from var(--oc-destructive) l c h / 0.1); }
|
|
37
|
+
|
|
38
|
+
/* Overflow trigger ("more" button, three dots drawn with CSS). Use popovertarget="<menu id>". */
|
|
39
|
+
.oc-table-actions-more {
|
|
40
|
+
display: inline-grid;
|
|
41
|
+
place-items: center;
|
|
42
|
+
inline-size: 1.75rem;
|
|
43
|
+
block-size: 1.75rem;
|
|
44
|
+
padding: 0;
|
|
45
|
+
color: var(--oc-muted-foreground);
|
|
46
|
+
background: none;
|
|
47
|
+
border: 1px solid transparent;
|
|
48
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
}
|
|
51
|
+
/* Three dots: one dot plus two box-shadow copies, all in currentColor. */
|
|
52
|
+
.oc-table-actions-more::before {
|
|
53
|
+
content: "";
|
|
54
|
+
inline-size: 0.2rem;
|
|
55
|
+
block-size: 0.2rem;
|
|
56
|
+
border-radius: 50%;
|
|
57
|
+
background: currentColor;
|
|
58
|
+
box-shadow: -0.35rem 0 0 currentColor, 0.35rem 0 0 currentColor;
|
|
59
|
+
}
|
|
60
|
+
.oc-table-actions-more:hover { color: var(--oc-foreground); background: var(--oc-accent); }
|
|
61
|
+
.oc-table-actions-more:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
62
|
+
/* While its menu is open the trigger stays highlighted (:has on the cell, popover is a sibling). */
|
|
63
|
+
.oc-table-actions:has(.oc-dropdown-menu:popover-open) .oc-table-actions-more { color: var(--oc-foreground); background: var(--oc-accent); border-color: var(--oc-border); }
|
|
64
|
+
|
|
65
|
+
/* The menu opens to the left side of the trigger, because the actions column sits at the row end. */
|
|
66
|
+
.oc-table-actions .oc-dropdown-menu[popover] { position-area: block-end span-inline-start; min-inline-size: 11rem; text-align: start; }
|
|
67
|
+
.oc-table-actions .oc-dropdown-item svg { inline-size: 1rem; block-size: 1rem; flex-shrink: 0; }
|
|
68
|
+
|
|
69
|
+
@media (prefers-reduced-motion: reduce) {
|
|
70
|
+
.oc-table-actions-icon { transition: none; }
|
|
71
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-table-compact.css | Map: wrapper (scroll, inside a card) -> header cells -> body cells (compact + striped) -> footer total -> modifiers (bordered, plain) */
|
|
2
|
+
/* Table compact: a small striped table for inside cards and side panels.
|
|
3
|
+
Wrap a normal <table class="oc-table"> in <div class="oc-table-compact">. The wrapper removes the
|
|
4
|
+
outer cell borders (the card gives the frame), shrinks padding and font, and stripes the rows.
|
|
5
|
+
Numbers: data-numeric on th / td (right aligned, tabular figures; from the table component). */
|
|
6
|
+
|
|
7
|
+
.oc-table-compact { inline-size: 100%; overflow-x: auto; }
|
|
8
|
+
.oc-table-compact > .oc-table { font-size: 0.8125rem; border-collapse: collapse; }
|
|
9
|
+
|
|
10
|
+
/* Cells: compact padding, only horizontal lines between rows. */
|
|
11
|
+
.oc-table-compact > .oc-table :is(th, td) {
|
|
12
|
+
padding: 0.3rem 0.625rem;
|
|
13
|
+
border: 0;
|
|
14
|
+
border-block-end: 1px solid var(--oc-border);
|
|
15
|
+
white-space: nowrap;
|
|
16
|
+
}
|
|
17
|
+
.oc-table-compact > .oc-table :is(th, td):first-child { padding-inline-start: 0.75rem; }
|
|
18
|
+
.oc-table-compact > .oc-table :is(th, td):last-child { padding-inline-end: 0.75rem; }
|
|
19
|
+
/* Long text columns may wrap: add data-wrap to the cell. */
|
|
20
|
+
.oc-table-compact > .oc-table :is(th, td)[data-wrap] { white-space: normal; }
|
|
21
|
+
|
|
22
|
+
/* Header: small muted labels on a quiet background. */
|
|
23
|
+
.oc-table-compact > .oc-table thead th {
|
|
24
|
+
font-size: 0.6875rem;
|
|
25
|
+
font-weight: 600;
|
|
26
|
+
letter-spacing: 0.04em;
|
|
27
|
+
text-transform: uppercase;
|
|
28
|
+
color: var(--oc-muted-foreground);
|
|
29
|
+
background: var(--oc-surface);
|
|
30
|
+
border-block-end-width: 1px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Stripes on every second body row; hover stays the table default. */
|
|
34
|
+
.oc-table-compact > .oc-table tbody tr:nth-child(even) { background: var(--oc-surface); }
|
|
35
|
+
.oc-table-compact > .oc-table tbody tr:last-child > td { border-block-end: 0; }
|
|
36
|
+
|
|
37
|
+
/* Footer row (total): bold, with a stronger line above it. */
|
|
38
|
+
.oc-table-compact > .oc-table tfoot :is(th, td) {
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
color: var(--oc-foreground);
|
|
41
|
+
background: none;
|
|
42
|
+
border-block-start: 2px solid var(--oc-border);
|
|
43
|
+
border-block-end: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Muted secondary text inside a cell ("per unit", sub line). */
|
|
47
|
+
.oc-table-compact-muted { color: var(--oc-muted-foreground); }
|
|
48
|
+
|
|
49
|
+
/* Bordered: own rounded frame (when the table is not inside a card). */
|
|
50
|
+
.oc-table-compact--bordered { border: 1px solid var(--oc-border); border-radius: var(--oc-radius); background: var(--oc-card); }
|
|
51
|
+
|
|
52
|
+
/* Plain: no stripes, no header background (very dense lists). */
|
|
53
|
+
.oc-table-compact--plain > .oc-table tbody tr:nth-child(even),
|
|
54
|
+
.oc-table-compact--plain > .oc-table thead th { background: none; }
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-top-progress.css | Map: bar (fixed, native progress) -> determinate value -> indeterminate animation -> color + static modifiers -> done state -> reduced motion */
|
|
2
|
+
/* Top progress: thin loading bar at the very top of the page, on a native <progress> element.
|
|
3
|
+
<progress class="oc-top-progress"> without value = indeterminate (sliding glow);
|
|
4
|
+
with value="40" max="100" = determinate. Hide it with the hidden attribute or .is-done (fades out). */
|
|
5
|
+
|
|
6
|
+
.oc-top-progress {
|
|
7
|
+
--oc-top-progress-color: var(--oc-primary);
|
|
8
|
+
--oc-top-progress-height: 3px;
|
|
9
|
+
position: fixed;
|
|
10
|
+
inset-block-start: 0;
|
|
11
|
+
inset-inline: 0;
|
|
12
|
+
z-index: 1000;
|
|
13
|
+
display: block;
|
|
14
|
+
inline-size: 100%;
|
|
15
|
+
block-size: var(--oc-top-progress-height);
|
|
16
|
+
margin: 0;
|
|
17
|
+
padding: 0;
|
|
18
|
+
border: 0;
|
|
19
|
+
appearance: none;
|
|
20
|
+
color: var(--oc-top-progress-color);
|
|
21
|
+
background: transparent;
|
|
22
|
+
pointer-events: none;
|
|
23
|
+
transition: opacity var(--oc-transition-normal);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Determinate value: the filled part, with a soft glow at its end. */
|
|
27
|
+
.oc-top-progress::-webkit-progress-bar { background: transparent; }
|
|
28
|
+
.oc-top-progress::-webkit-progress-value {
|
|
29
|
+
background: var(--oc-top-progress-color);
|
|
30
|
+
box-shadow: 0 0 8px var(--oc-top-progress-color);
|
|
31
|
+
transition: inline-size var(--oc-transition-normal);
|
|
32
|
+
}
|
|
33
|
+
.oc-top-progress::-moz-progress-bar { background: var(--oc-top-progress-color); box-shadow: 0 0 8px var(--oc-top-progress-color); }
|
|
34
|
+
|
|
35
|
+
/* Indeterminate (no value): a segment slides from start to end, again and again. */
|
|
36
|
+
.oc-top-progress:indeterminate {
|
|
37
|
+
background: linear-gradient(90deg, transparent, var(--oc-top-progress-color) 30% 70%, transparent) no-repeat;
|
|
38
|
+
background-size: 35% 100%;
|
|
39
|
+
animation: oc-top-progress-slide 1.2s ease-in-out infinite;
|
|
40
|
+
}
|
|
41
|
+
.oc-top-progress:indeterminate::-webkit-progress-bar { background: transparent; }
|
|
42
|
+
.oc-top-progress:indeterminate::-moz-progress-bar { background: transparent; box-shadow: none; }
|
|
43
|
+
@keyframes oc-top-progress-slide {
|
|
44
|
+
from { background-position: -40% 0; }
|
|
45
|
+
to { background-position: 140% 0; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Color modifiers (theme tokens). */
|
|
49
|
+
.oc-top-progress--success { --oc-top-progress-color: var(--oc-success); }
|
|
50
|
+
.oc-top-progress--warning { --oc-top-progress-color: var(--oc-warning); }
|
|
51
|
+
.oc-top-progress--danger { --oc-top-progress-color: var(--oc-destructive); }
|
|
52
|
+
.oc-top-progress--brand { --oc-top-progress-color: var(--oc-brand-1); }
|
|
53
|
+
|
|
54
|
+
/* Thicker bar. */
|
|
55
|
+
.oc-top-progress--lg { --oc-top-progress-height: 4px; }
|
|
56
|
+
|
|
57
|
+
/* Static: stays in the normal flow (inside a card, header or demo) instead of fixed to the window. */
|
|
58
|
+
.oc-top-progress--static { position: relative; inset: auto; z-index: auto; background-color: var(--oc-surface-strong); }
|
|
59
|
+
|
|
60
|
+
/* Done: fades out, then the app can remove it or set hidden. */
|
|
61
|
+
.oc-top-progress.is-done { opacity: 0; }
|
|
62
|
+
.oc-top-progress[hidden] { display: none; }
|
|
63
|
+
|
|
64
|
+
@media (prefers-reduced-motion: reduce) {
|
|
65
|
+
.oc-top-progress:indeterminate { animation-duration: 3s; }
|
|
66
|
+
.oc-top-progress,
|
|
67
|
+
.oc-top-progress::-webkit-progress-value { transition: none; }
|
|
68
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-tree.css | Map: wrapper (scroll box) -> head row -> lists + indent -> row -> chevron (details) -> label + icon -> sticky actions -> states -> sticky modifier */
|
|
2
|
+
/* Tree: nested list of folders (<details>) and leaves. Names may scroll horizontally;
|
|
3
|
+
the action column (.oc-tree-actions) stays pinned to the end edge with position: sticky.
|
|
4
|
+
Open / close is native <details>; no JavaScript. Selected row: aria-current="true" or .is-selected. */
|
|
5
|
+
|
|
6
|
+
/* Scroll box around the whole tree. Card look by default. */
|
|
7
|
+
.oc-tree {
|
|
8
|
+
--oc-tree-row: 2rem;
|
|
9
|
+
--oc-tree-indent: 1rem;
|
|
10
|
+
--oc-tree-bg: var(--oc-card);
|
|
11
|
+
overflow: auto;
|
|
12
|
+
max-block-size: var(--oc-tree-height, none);
|
|
13
|
+
font-size: 0.875rem;
|
|
14
|
+
color: var(--oc-foreground);
|
|
15
|
+
background: var(--oc-tree-bg);
|
|
16
|
+
border: 1px solid var(--oc-border);
|
|
17
|
+
border-radius: var(--oc-radius);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Optional head row (root of the tree, e.g. "All projects"). Sticks to the top while scrolling. */
|
|
21
|
+
.oc-tree-head {
|
|
22
|
+
position: sticky;
|
|
23
|
+
inset-block-start: 0;
|
|
24
|
+
z-index: 2;
|
|
25
|
+
font-weight: 600;
|
|
26
|
+
background: var(--oc-tree-bg);
|
|
27
|
+
border-block-end: 1px solid var(--oc-border);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Lists: no bullets; each nested list moves one indent step inward. */
|
|
31
|
+
.oc-tree-list { margin: 0; padding: 0.25rem 0; list-style: none; min-inline-size: max-content; }
|
|
32
|
+
.oc-tree-list .oc-tree-list { padding-block: 0; padding-inline-start: var(--oc-tree-indent); }
|
|
33
|
+
|
|
34
|
+
/* One row: chevron + icon + label, then the actions. Works on <summary>, <div>, <a> or <button>. */
|
|
35
|
+
.oc-tree-row {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: 0.375rem;
|
|
39
|
+
min-block-size: var(--oc-tree-row);
|
|
40
|
+
padding-inline: 0.5rem 0;
|
|
41
|
+
color: inherit;
|
|
42
|
+
text-decoration: none;
|
|
43
|
+
list-style: none;
|
|
44
|
+
}
|
|
45
|
+
summary.oc-tree-row { cursor: pointer; }
|
|
46
|
+
.oc-tree-row::-webkit-details-marker { display: none; }
|
|
47
|
+
.oc-tree-row:hover { background: var(--oc-accent); }
|
|
48
|
+
.oc-tree-row:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
|
|
49
|
+
|
|
50
|
+
/* Chevron: drawn with borders. Folders rotate it when open; leaves keep an empty spacer. */
|
|
51
|
+
.oc-tree-row::before {
|
|
52
|
+
content: "";
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
inline-size: 0.4rem;
|
|
55
|
+
block-size: 0.4rem;
|
|
56
|
+
margin-inline: 0.2rem 0.35rem;
|
|
57
|
+
border-inline-end: 1.5px solid currentColor;
|
|
58
|
+
border-block-end: 1.5px solid currentColor;
|
|
59
|
+
color: var(--oc-muted-foreground);
|
|
60
|
+
rotate: -45deg;
|
|
61
|
+
transition: rotate var(--oc-transition-fast);
|
|
62
|
+
}
|
|
63
|
+
.oc-tree-node[open] > .oc-tree-row::before { rotate: 45deg; }
|
|
64
|
+
.oc-tree-row:not(summary)::before { visibility: hidden; }
|
|
65
|
+
|
|
66
|
+
/* Icon and label. The label never wraps; long names make the box scroll sideways. */
|
|
67
|
+
.oc-tree-icon { display: inline-flex; flex-shrink: 0; color: var(--oc-muted-foreground); }
|
|
68
|
+
.oc-tree-icon svg { inline-size: 1rem; block-size: 1rem; }
|
|
69
|
+
.oc-tree-label { flex: 1 0 auto; white-space: nowrap; padding-inline-end: 0.75rem; color: inherit; text-decoration: none; }
|
|
70
|
+
/* A leaf row is a div; its label may be a link (no buttons inside links). */
|
|
71
|
+
a.oc-tree-label:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; border-radius: 2px; }
|
|
72
|
+
.oc-tree-count { margin-inline-end: 0.5rem; font-size: 0.75rem; color: var(--oc-muted-foreground); font-variant-numeric: tabular-nums; }
|
|
73
|
+
|
|
74
|
+
/* Actions: pinned to the end edge, with the row background behind it so names slide underneath. */
|
|
75
|
+
.oc-tree-actions {
|
|
76
|
+
position: sticky;
|
|
77
|
+
inset-inline-end: 0;
|
|
78
|
+
z-index: 1;
|
|
79
|
+
display: inline-flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: 0.125rem;
|
|
82
|
+
margin-inline-start: auto;
|
|
83
|
+
padding-inline: 0.5rem 0.375rem;
|
|
84
|
+
background: var(--oc-tree-bg);
|
|
85
|
+
box-shadow: -0.75rem 0 0.75rem -0.75rem oklch(from var(--color-black-60) l c h / 0.25);
|
|
86
|
+
}
|
|
87
|
+
.oc-tree-row:hover > .oc-tree-actions { background: linear-gradient(var(--oc-accent), var(--oc-accent)), var(--oc-tree-bg); }
|
|
88
|
+
/* Hide actions until hover or focus (optional): .oc-tree--quiet */
|
|
89
|
+
.oc-tree--quiet .oc-tree-actions { opacity: 0; }
|
|
90
|
+
.oc-tree--quiet .oc-tree-row:is(:hover, :focus-within) > .oc-tree-actions { opacity: 1; }
|
|
91
|
+
|
|
92
|
+
/* Selected row: tinted with the primary token. */
|
|
93
|
+
.oc-tree-row:is([aria-current="true"], .is-selected, :has(> .oc-tree-label[aria-current="true"])) { color: var(--oc-primary); font-weight: 500; background: oklch(from var(--oc-primary) l c h / 0.1); }
|
|
94
|
+
.oc-tree-row:is([aria-current="true"], .is-selected, :has(> .oc-tree-label[aria-current="true"])) > .oc-tree-actions { background: linear-gradient(oklch(from var(--oc-primary) l c h / 0.1), oklch(from var(--oc-primary) l c h / 0.1)), var(--oc-tree-bg); }
|
|
95
|
+
/* Muted row (archived, empty folder). */
|
|
96
|
+
.oc-tree-row.is-muted { color: var(--oc-muted-foreground); }
|
|
97
|
+
|
|
98
|
+
/* Flush: no frame, for use inside a card or sidebar. */
|
|
99
|
+
.oc-tree--flush { border: 0; border-radius: 0; --oc-tree-bg: transparent; }
|
|
100
|
+
.oc-tree--flush .oc-tree-actions { background: var(--oc-card); }
|
|
101
|
+
|
|
102
|
+
@media (prefers-reduced-motion: reduce) {
|
|
103
|
+
.oc-tree-row::before { transition: none; }
|
|
104
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-upgrade-banner.css | Map: callout -> icon -> body (title + plan badge, text) -> usage meter -> actions + dismiss -> modifiers (premium, compact) */
|
|
2
|
+
/* Upgrade banner: soft callout for a free plan with limits ("3 of 5 projects used - upgrade").
|
|
3
|
+
Usage is a native <meter> element. Tone follows --oc-upgrade-color (warning by default). */
|
|
4
|
+
|
|
5
|
+
.oc-upgrade-banner {
|
|
6
|
+
--oc-upgrade-color: var(--oc-warning);
|
|
7
|
+
position: relative;
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-wrap: wrap;
|
|
10
|
+
align-items: flex-start;
|
|
11
|
+
gap: 0.75rem 1rem;
|
|
12
|
+
padding: 1rem 1.25rem;
|
|
13
|
+
font-size: 0.875rem;
|
|
14
|
+
color: var(--oc-foreground);
|
|
15
|
+
background: color-mix(in oklch, var(--oc-upgrade-color) 8%, var(--oc-card));
|
|
16
|
+
border: 1px solid oklch(from var(--oc-upgrade-color) l c h / 0.35);
|
|
17
|
+
border-radius: calc(var(--oc-radius) + 4px);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Icon (sparkle, crown, lock) in the tone color. */
|
|
21
|
+
.oc-upgrade-banner-icon { display: inline-flex; flex-shrink: 0; color: var(--oc-upgrade-color); }
|
|
22
|
+
.oc-upgrade-banner-icon svg { inline-size: 1.25rem; block-size: 1.25rem; }
|
|
23
|
+
|
|
24
|
+
/* Text column. */
|
|
25
|
+
.oc-upgrade-banner-body { flex: 1 1 18rem; min-inline-size: 0; }
|
|
26
|
+
.oc-upgrade-banner-title { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0; font-size: 0.9375rem; font-weight: 600; }
|
|
27
|
+
.oc-upgrade-banner-plan {
|
|
28
|
+
padding: 0.125rem 0.5rem;
|
|
29
|
+
font-size: 0.6875rem;
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
color: var(--oc-upgrade-color);
|
|
32
|
+
background: oklch(from var(--oc-upgrade-color) l c h / 0.14);
|
|
33
|
+
border-radius: 999px;
|
|
34
|
+
}
|
|
35
|
+
.oc-upgrade-banner-text { margin: 0.25rem 0 0; line-height: 1.55; color: var(--oc-muted-foreground); }
|
|
36
|
+
.oc-upgrade-banner-text a { color: var(--oc-primary); }
|
|
37
|
+
|
|
38
|
+
/* Usage row: label + <meter> + "3 / 5". */
|
|
39
|
+
.oc-upgrade-banner-usage { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 0.5rem; margin-block-start: 0.625rem; font-size: 0.75rem; color: var(--oc-muted-foreground); }
|
|
40
|
+
.oc-upgrade-banner-meter {
|
|
41
|
+
inline-size: 100%;
|
|
42
|
+
block-size: 0.375rem;
|
|
43
|
+
appearance: none;
|
|
44
|
+
background: var(--oc-surface-strong);
|
|
45
|
+
border: 0;
|
|
46
|
+
border-radius: 999px;
|
|
47
|
+
}
|
|
48
|
+
.oc-upgrade-banner-meter::-webkit-meter-bar { background: var(--oc-surface-strong); border: 0; border-radius: 999px; block-size: 0.375rem; }
|
|
49
|
+
.oc-upgrade-banner-meter::-webkit-meter-optimum-value,
|
|
50
|
+
.oc-upgrade-banner-meter::-webkit-meter-suboptimum-value,
|
|
51
|
+
.oc-upgrade-banner-meter::-webkit-meter-even-less-good-value { background: var(--oc-upgrade-color); border-radius: 999px; }
|
|
52
|
+
.oc-upgrade-banner-meter::-moz-meter-bar { background: var(--oc-upgrade-color); border-radius: 999px; }
|
|
53
|
+
|
|
54
|
+
/* Actions: upgrade button (+ secondary link). */
|
|
55
|
+
.oc-upgrade-banner-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; align-self: center; }
|
|
56
|
+
|
|
57
|
+
/* Dismiss (X) in the top corner; the app hides the banner (hidden attribute). */
|
|
58
|
+
.oc-upgrade-banner-dismiss {
|
|
59
|
+
position: absolute;
|
|
60
|
+
inset-block-start: 0.5rem;
|
|
61
|
+
inset-inline-end: 0.5rem;
|
|
62
|
+
display: grid;
|
|
63
|
+
place-items: center;
|
|
64
|
+
inline-size: 1.75rem;
|
|
65
|
+
block-size: 1.75rem;
|
|
66
|
+
padding: 0;
|
|
67
|
+
font-size: 1.1rem;
|
|
68
|
+
line-height: 1;
|
|
69
|
+
color: var(--oc-muted-foreground);
|
|
70
|
+
background: none;
|
|
71
|
+
border: 0;
|
|
72
|
+
border-radius: var(--oc-radius);
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
}
|
|
75
|
+
.oc-upgrade-banner-dismiss:hover { color: var(--oc-foreground); background: var(--oc-accent); }
|
|
76
|
+
.oc-upgrade-banner-dismiss:focus-visible { outline: 2px solid var(--oc-ring); }
|
|
77
|
+
.oc-upgrade-banner:has(.oc-upgrade-banner-dismiss) { padding-inline-end: 2.75rem; }
|
|
78
|
+
.oc-upgrade-banner[hidden] { display: none; }
|
|
79
|
+
|
|
80
|
+
/* Premium: brand gradient frame and primary tone. */
|
|
81
|
+
.oc-upgrade-banner--premium {
|
|
82
|
+
--oc-upgrade-color: var(--oc-primary);
|
|
83
|
+
border-color: transparent;
|
|
84
|
+
background: linear-gradient(color-mix(in oklch, var(--oc-primary) 6%, var(--oc-card)), color-mix(in oklch, var(--oc-primary) 6%, var(--oc-card))) padding-box, var(--oc-gradient-brand) border-box;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Compact: one line (title + text inline), smaller padding. */
|
|
88
|
+
.oc-upgrade-banner--compact { align-items: center; padding: 0.5rem 0.75rem; }
|
|
89
|
+
.oc-upgrade-banner--compact .oc-upgrade-banner-body { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; }
|
|
90
|
+
.oc-upgrade-banner--compact .oc-upgrade-banner-text { margin: 0; }
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-version-banner.css | Map: chip -> icon + text (long / short) -> action -> tone modifiers -> floating modifier -> entry animation -> reduced motion */
|
|
2
|
+
/* Version banner: small "New version available - reload" chip, e.g. at the end of the app header.
|
|
3
|
+
Reload without JavaScript: the action is a link with href="" (the current page).
|
|
4
|
+
Show it only when your app detects a newer build (remove the hidden attribute). */
|
|
5
|
+
|
|
6
|
+
.oc-version-banner {
|
|
7
|
+
--oc-version-banner-color: var(--oc-warning);
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: 0.5rem;
|
|
11
|
+
min-inline-size: 0;
|
|
12
|
+
max-inline-size: 100%;
|
|
13
|
+
padding: 0.25rem 0.25rem 0.25rem 0.625rem;
|
|
14
|
+
font-size: 0.75rem;
|
|
15
|
+
line-height: 1.3;
|
|
16
|
+
color: var(--oc-foreground);
|
|
17
|
+
background: oklch(from var(--oc-version-banner-color) l c h / 0.12);
|
|
18
|
+
border: 1px solid oklch(from var(--oc-version-banner-color) l c h / 0.4);
|
|
19
|
+
border-radius: 999px;
|
|
20
|
+
transition: opacity var(--oc-transition-normal), translate var(--oc-transition-normal);
|
|
21
|
+
@starting-style { opacity: 0; translate: 0 -0.25rem; }
|
|
22
|
+
}
|
|
23
|
+
.oc-version-banner[hidden] { display: none; }
|
|
24
|
+
|
|
25
|
+
/* Small dot in the tone color (pulses gently). */
|
|
26
|
+
.oc-version-banner-dot {
|
|
27
|
+
flex-shrink: 0;
|
|
28
|
+
inline-size: 0.5rem;
|
|
29
|
+
block-size: 0.5rem;
|
|
30
|
+
border-radius: 50%;
|
|
31
|
+
background: var(--oc-version-banner-color);
|
|
32
|
+
animation: oc-version-banner-pulse 2s ease-in-out infinite;
|
|
33
|
+
}
|
|
34
|
+
@keyframes oc-version-banner-pulse { 50% { box-shadow: 0 0 0 4px oklch(from var(--oc-version-banner-color) l c h / 0.2); } }
|
|
35
|
+
|
|
36
|
+
/* Text: the long text hides on small screens, the short one shows instead. */
|
|
37
|
+
.oc-version-banner-text { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
38
|
+
.oc-version-banner-short { display: none; white-space: nowrap; }
|
|
39
|
+
@media (max-width: 40rem) {
|
|
40
|
+
.oc-version-banner-text:has(+ .oc-version-banner-short) { display: none; }
|
|
41
|
+
.oc-version-banner-short { display: inline; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Action (link or button): compact pill button. */
|
|
45
|
+
.oc-version-banner-action {
|
|
46
|
+
flex-shrink: 0;
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
gap: 0.25rem;
|
|
50
|
+
padding: 0.25rem 0.625rem;
|
|
51
|
+
font: inherit;
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
color: var(--oc-foreground);
|
|
54
|
+
text-decoration: none;
|
|
55
|
+
background: var(--oc-background);
|
|
56
|
+
border: 1px solid oklch(from var(--oc-version-banner-color) l c h / 0.4);
|
|
57
|
+
border-radius: 999px;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
}
|
|
60
|
+
.oc-version-banner-action:hover { background: oklch(from var(--oc-version-banner-color) l c h / 0.15); }
|
|
61
|
+
.oc-version-banner-action:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
62
|
+
|
|
63
|
+
/* Tone modifiers. */
|
|
64
|
+
.oc-version-banner--info { --oc-version-banner-color: var(--oc-primary); }
|
|
65
|
+
.oc-version-banner--success { --oc-version-banner-color: var(--oc-success); }
|
|
66
|
+
|
|
67
|
+
/* Floating: fixed at the bottom end of the window (for apps without a header slot). */
|
|
68
|
+
.oc-version-banner--floating {
|
|
69
|
+
position: fixed;
|
|
70
|
+
inset-block-end: 1rem;
|
|
71
|
+
inset-inline-end: 1rem;
|
|
72
|
+
z-index: 900;
|
|
73
|
+
background: var(--oc-popover);
|
|
74
|
+
box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@media (prefers-reduced-motion: reduce) {
|
|
78
|
+
.oc-version-banner { transition: none; }
|
|
79
|
+
.oc-version-banner-dot { animation: none; }
|
|
80
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/data-chart-line.css | Map: figure (head, legend) -> plot (y axis, gridlines, svg) -> series colors
|
|
2
|
+
-> line + area paths -> hover columns (cursor line, dots, tooltip) -> x axis -> modifiers (--area, --dots, --spark) -> reduced motion
|
|
3
|
+
Line and area chart: an inline SVG in a 0..100 viewBox, stretched to the plot (preserveAspectRatio="none", strokes keep their
|
|
4
|
+
width with vector-effect). Data points are HTML columns laid over the plot at --x / --n, each with one dot per series at --y
|
|
5
|
+
(percent of the plot height) and a tooltip that opens on :hover and :focus-visible. Colors are the --oc-chart-1..5 tokens. */
|
|
6
|
+
|
|
7
|
+
.oc-chart-line {
|
|
8
|
+
--oc-chart-line-height: 12rem;
|
|
9
|
+
display: grid;
|
|
10
|
+
gap: 0.75rem;
|
|
11
|
+
inline-size: 100%;
|
|
12
|
+
min-inline-size: 0;
|
|
13
|
+
margin: 0;
|
|
14
|
+
color: var(--oc-foreground);
|
|
15
|
+
font-size: 0.75rem;
|
|
16
|
+
}
|
|
17
|
+
.oc-chart-line__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.25rem 1rem; }
|
|
18
|
+
.oc-chart-line__title { margin: 0; font-size: 0.95rem; font-weight: 650; }
|
|
19
|
+
.oc-chart-line__sub { margin: 0; color: var(--oc-muted-foreground); font-size: 0.8rem; }
|
|
20
|
+
.oc-chart-line__value { font-size: 1.5rem; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
|
|
21
|
+
|
|
22
|
+
/* ---- Body: y axis | plot, x axis below the plot ---- */
|
|
23
|
+
.oc-chart-line__body { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 0.5rem; row-gap: 0.4rem; }
|
|
24
|
+
.oc-chart-line__y {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
block-size: var(--oc-chart-line-height);
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 0;
|
|
31
|
+
list-style: none;
|
|
32
|
+
color: var(--oc-chart-axis, var(--oc-muted-foreground));
|
|
33
|
+
font-variant-numeric: tabular-nums;
|
|
34
|
+
line-height: 1;
|
|
35
|
+
text-align: end;
|
|
36
|
+
}
|
|
37
|
+
/* Zero-height labels: space-between puts their centres exactly on the gridlines */
|
|
38
|
+
.oc-chart-line__y > li { display: flex; align-items: center; justify-content: flex-end; block-size: 0; }
|
|
39
|
+
|
|
40
|
+
.oc-chart-line__plot {
|
|
41
|
+
--oc-chart-line-rows: 4;
|
|
42
|
+
position: relative;
|
|
43
|
+
block-size: var(--oc-chart-line-height);
|
|
44
|
+
border-block-end: 1px solid var(--oc-chart-axis, var(--oc-border));
|
|
45
|
+
background: repeating-linear-gradient(to top, transparent 0 calc(100% / var(--oc-chart-line-rows) - 1px), var(--oc-chart-grid, var(--oc-border)) calc(100% / var(--oc-chart-line-rows) - 1px) calc(100% / var(--oc-chart-line-rows)));
|
|
46
|
+
}
|
|
47
|
+
.oc-chart-line__svg { position: absolute; inset: 0; display: block; inline-size: 100%; block-size: 100%; overflow: visible; }
|
|
48
|
+
|
|
49
|
+
/* ---- Series: one g per series, colored by position ---- */
|
|
50
|
+
.oc-chart-line__series { --oc-chart-color: var(--oc-chart-1); }
|
|
51
|
+
.oc-chart-line__series:nth-of-type(2) { --oc-chart-color: var(--oc-chart-2); }
|
|
52
|
+
.oc-chart-line__series:nth-of-type(3) { --oc-chart-color: var(--oc-chart-3); }
|
|
53
|
+
.oc-chart-line__series:nth-of-type(4) { --oc-chart-color: var(--oc-chart-4); }
|
|
54
|
+
.oc-chart-line__series:nth-of-type(5) { --oc-chart-color: var(--oc-chart-5); }
|
|
55
|
+
.oc-chart-line__path {
|
|
56
|
+
fill: none;
|
|
57
|
+
stroke: var(--oc-chart-color);
|
|
58
|
+
stroke-width: 2.25;
|
|
59
|
+
stroke-linecap: round;
|
|
60
|
+
stroke-linejoin: round;
|
|
61
|
+
vector-effect: non-scaling-stroke;
|
|
62
|
+
}
|
|
63
|
+
.oc-chart-line__area { display: none; fill: var(--oc-chart-color); fill-opacity: 0.16; stroke: none; }
|
|
64
|
+
.oc-chart-line--area .oc-chart-line__area { display: inline; }
|
|
65
|
+
.oc-chart-line__path--dashed { stroke-dasharray: 5 4; }
|
|
66
|
+
|
|
67
|
+
/* ---- Hover columns: one per x value; the column is the hit area, the dots and tip live inside ---- */
|
|
68
|
+
.oc-chart-line__cols { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }
|
|
69
|
+
.oc-chart-line__col {
|
|
70
|
+
position: absolute;
|
|
71
|
+
inset-block: 0;
|
|
72
|
+
inset-inline-start: calc(var(--x) / (var(--n) - 1) * 100%);
|
|
73
|
+
inline-size: calc(100% / (var(--n) - 1));
|
|
74
|
+
translate: -50% 0;
|
|
75
|
+
outline: 0;
|
|
76
|
+
cursor: crosshair;
|
|
77
|
+
}
|
|
78
|
+
.oc-chart-line__col::before {
|
|
79
|
+
content: "";
|
|
80
|
+
position: absolute;
|
|
81
|
+
inset-block: 0;
|
|
82
|
+
inset-inline-start: 50%;
|
|
83
|
+
border-inline-start: 1px dashed var(--oc-chart-axis, var(--oc-muted-foreground));
|
|
84
|
+
opacity: 0;
|
|
85
|
+
}
|
|
86
|
+
.oc-chart-line__dot {
|
|
87
|
+
--oc-chart-color: var(--oc-chart-1);
|
|
88
|
+
position: absolute;
|
|
89
|
+
inset-block-end: var(--y);
|
|
90
|
+
inset-inline-start: 50%;
|
|
91
|
+
inline-size: 0.6rem;
|
|
92
|
+
block-size: 0.6rem;
|
|
93
|
+
border: 2px solid var(--oc-card);
|
|
94
|
+
border-radius: 50%;
|
|
95
|
+
background: var(--oc-chart-color);
|
|
96
|
+
translate: -50% 50%;
|
|
97
|
+
scale: 0;
|
|
98
|
+
transition: scale var(--oc-transition-fast);
|
|
99
|
+
}
|
|
100
|
+
.oc-chart-line__dot:nth-of-type(2) { --oc-chart-color: var(--oc-chart-2); }
|
|
101
|
+
.oc-chart-line__dot:nth-of-type(3) { --oc-chart-color: var(--oc-chart-3); }
|
|
102
|
+
.oc-chart-line__dot:nth-of-type(4) { --oc-chart-color: var(--oc-chart-4); }
|
|
103
|
+
.oc-chart-line__dot:nth-of-type(5) { --oc-chart-color: var(--oc-chart-5); }
|
|
104
|
+
|
|
105
|
+
/* Tooltip: slides from left-aligned (first point) to right-aligned (last point), so it never leaves the plot */
|
|
106
|
+
.oc-chart-line__tip {
|
|
107
|
+
position: absolute;
|
|
108
|
+
z-index: 2;
|
|
109
|
+
inset-block-start: 0.25rem;
|
|
110
|
+
inset-inline-start: 50%;
|
|
111
|
+
display: grid;
|
|
112
|
+
gap: 0.3rem;
|
|
113
|
+
min-inline-size: 8.5rem;
|
|
114
|
+
padding: 0.45rem 0.6rem;
|
|
115
|
+
border: 1px solid var(--oc-border);
|
|
116
|
+
border-radius: var(--oc-radius);
|
|
117
|
+
background: var(--oc-popover, var(--oc-card));
|
|
118
|
+
box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.14);
|
|
119
|
+
color: var(--oc-popover-foreground, var(--oc-foreground));
|
|
120
|
+
white-space: nowrap;
|
|
121
|
+
pointer-events: none;
|
|
122
|
+
translate: calc(var(--x) / (var(--n) - 1) * -100%) 0;
|
|
123
|
+
opacity: 0;
|
|
124
|
+
visibility: hidden;
|
|
125
|
+
transition: opacity var(--oc-transition-fast), visibility var(--oc-transition-fast);
|
|
126
|
+
}
|
|
127
|
+
.oc-chart-line__tip > strong { font-weight: 650; }
|
|
128
|
+
.oc-chart-line__row { display: flex; align-items: center; gap: 0.45rem; color: var(--oc-muted-foreground); }
|
|
129
|
+
.oc-chart-line__row::before { content: ""; inline-size: 0.6rem; block-size: 0.6rem; flex: none; border-radius: 2px; background: var(--oc-chart-color, var(--oc-chart-1)); }
|
|
130
|
+
.oc-chart-line__row > b { margin-inline-start: auto; padding-inline-start: 1rem; color: var(--oc-foreground); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
131
|
+
.oc-chart-line__row:nth-of-type(1) { --oc-chart-color: var(--oc-chart-1); }
|
|
132
|
+
.oc-chart-line__row:nth-of-type(2) { --oc-chart-color: var(--oc-chart-2); }
|
|
133
|
+
.oc-chart-line__row:nth-of-type(3) { --oc-chart-color: var(--oc-chart-3); }
|
|
134
|
+
.oc-chart-line__row:nth-of-type(4) { --oc-chart-color: var(--oc-chart-4); }
|
|
135
|
+
.oc-chart-line__row:nth-of-type(5) { --oc-chart-color: var(--oc-chart-5); }
|
|
136
|
+
|
|
137
|
+
.oc-chart-line__col:is(:hover, :focus-visible)::before { opacity: 1; }
|
|
138
|
+
.oc-chart-line__col:is(:hover, :focus-visible) > .oc-chart-line__dot { scale: 1; }
|
|
139
|
+
.oc-chart-line__col:is(:hover, :focus-visible) > .oc-chart-line__tip { opacity: 1; visibility: visible; }
|
|
140
|
+
.oc-chart-line__col:focus-visible::before { border-inline-start: 2px solid var(--oc-ring); }
|
|
141
|
+
|
|
142
|
+
/* ---- X axis ---- */
|
|
143
|
+
.oc-chart-line__x {
|
|
144
|
+
grid-column: 2;
|
|
145
|
+
display: flex;
|
|
146
|
+
justify-content: space-between;
|
|
147
|
+
margin: 0;
|
|
148
|
+
padding: 0;
|
|
149
|
+
list-style: none;
|
|
150
|
+
color: var(--oc-chart-axis, var(--oc-muted-foreground));
|
|
151
|
+
}
|
|
152
|
+
.oc-chart-line__x > li { flex: 0 0 0; display: flex; justify-content: center; white-space: nowrap; }
|
|
153
|
+
.oc-chart-line__x > li:first-child { justify-content: flex-start; }
|
|
154
|
+
.oc-chart-line__x > li:last-child { justify-content: flex-end; }
|
|
155
|
+
|
|
156
|
+
/* ---- Modifiers ---- */
|
|
157
|
+
.oc-chart-line--dots .oc-chart-line__dot { scale: 0.8; }
|
|
158
|
+
.oc-chart-line--dots .oc-chart-line__col:is(:hover, :focus-visible) > .oc-chart-line__dot { scale: 1.15; }
|
|
159
|
+
/* Sparkline: no axes, no grid; for stat cards and table cells */
|
|
160
|
+
.oc-chart-line--spark { --oc-chart-line-height: 3rem; gap: 0.25rem; }
|
|
161
|
+
.oc-chart-line--spark .oc-chart-line__body { grid-template-columns: minmax(0, 1fr); }
|
|
162
|
+
.oc-chart-line--spark .oc-chart-line__plot { border: 0; background: none; }
|
|
163
|
+
.oc-chart-line--spark .oc-chart-line__tip { inset-block-start: auto; inset-block-end: calc(100% + 0.4rem); min-inline-size: 0; }
|
|
164
|
+
|
|
165
|
+
@media (prefers-reduced-motion: reduce) {
|
|
166
|
+
.oc-chart-line__dot, .oc-chart-line__tip { transition: none; }
|
|
167
|
+
}
|