@birdycom/omniaui 0.12.5 → 0.13.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/README.md +1 -1
- package/bin/lib/copy-js.mjs +12 -1
- package/bin/omniaui.mjs +2 -2
- package/dist/barcode.d.ts +15 -0
- package/dist/barcode.js +164 -0
- package/dist/components-extra.css +15 -1
- package/dist/components.css +2237 -256
- package/dist/components.min.css +1 -1
- package/dist/contact-form.d.ts +5 -0
- package/dist/contact-form.js +138 -0
- package/dist/data-table.js +135 -98
- package/dist/fly.d.ts +4 -0
- package/dist/fly.js +80 -0
- package/dist/form-extras.d.ts +11 -0
- package/dist/form-extras.js +214 -0
- package/dist/lazy.d.ts +5 -0
- package/dist/lazy.js +37 -0
- package/dist/oc-theme.css +3 -1
- package/dist/parts/app-data-table-footer.css +38 -0
- package/dist/parts/app-data-table-rich.css +161 -0
- package/dist/parts/app-data-table.css +44 -74
- package/dist/parts/app-name-card.css +65 -0
- package/dist/parts/app-result.css +39 -0
- package/dist/parts/app-titlebar.css +16 -8
- package/dist/parts/data-meter-row.css +40 -0
- package/dist/parts/data-price.css +20 -0
- package/dist/parts/data-ribbon.css +76 -0
- package/dist/parts/data-spec-list.css +36 -0
- package/dist/parts/data-stock.css +41 -0
- package/dist/parts/forms-affix-input.css +1 -1
- package/dist/parts/forms-avatar-picker.css +43 -0
- package/dist/parts/forms-date-field.css +53 -0
- package/dist/parts/forms-density.css +13 -0
- package/dist/parts/forms-form-section.css +64 -0
- package/dist/parts/forms-form.css +76 -0
- package/dist/parts/forms-quantity.css +55 -0
- package/dist/parts/forms-rating.css +81 -0
- package/dist/parts/media-attachment.css +1 -1
- package/dist/parts/media-barcode.css +23 -0
- package/dist/parts/media-chat-composer.css +3 -0
- package/dist/parts/media-emoji-picker.css +3 -1
- package/dist/parts/media-gallery.css +55 -0
- package/dist/parts/media-glass-liquid.css +99 -0
- package/dist/parts/media-glass-options.css +50 -0
- package/dist/parts/media-lazy.css +46 -0
- package/dist/parts/media-lightbox.css +4 -0
- package/dist/parts/media-message-thread.css +1 -1
- package/dist/parts/media-message.css +1 -1
- package/dist/parts/nav-topbar-variants.css +96 -0
- package/dist/parts/site-base.css +33 -0
- package/dist/parts/site-brand-mark.css +56 -0
- package/dist/parts/site-cards.css +183 -0
- package/dist/parts/site-fly.css +40 -0
- package/dist/parts/site-header-variants.css +70 -0
- package/dist/parts/site-layout.css +69 -0
- package/dist/parts/site-panel.css +29 -0
- package/dist/parts/site-prose.css +13 -1
- package/dist/parts/utility-hover.css +44 -0
- package/dist/showcase.css +114 -22
- package/manifest.json +193 -9
- package/package.json +7 -2
- package/snippets/accordion-accent.html +1 -1
- package/snippets/attachment.html +4 -4
- package/snippets/avatar-picker.html +40 -0
- package/snippets/barcode.html +20 -0
- package/snippets/brand-mark.html +41 -0
- package/snippets/chat-composer.html +3 -3
- package/snippets/data-table.html +51 -76
- package/snippets/date-field.html +44 -0
- package/snippets/doc-viewer.html +1 -1
- package/snippets/emoji-picker.html +1 -1
- package/snippets/feature-card.html +52 -0
- package/snippets/fly-in.html +30 -0
- package/snippets/form-density.html +25 -0
- package/snippets/form-section.html +47 -0
- package/snippets/gallery.html +25 -0
- package/snippets/hover-effects.html +19 -0
- package/snippets/icon-list.html +28 -0
- package/snippets/lazy.html +39 -0
- package/snippets/message-thread.html +12 -12
- package/snippets/message.html +10 -10
- package/snippets/meter-row.html +29 -0
- package/snippets/name-card.html +72 -0
- package/snippets/page-header.html +1 -1
- package/snippets/page-sections.html +50 -0
- package/snippets/panel.html +18 -0
- package/snippets/price.html +25 -0
- package/snippets/prose.html +3 -3
- package/snippets/quantity.html +12 -0
- package/snippets/rating.html +26 -0
- package/snippets/result.html +31 -0
- package/snippets/ribbon.html +48 -0
- package/snippets/shot-grid.html +27 -0
- package/snippets/sidebar.html +3 -3
- package/snippets/spec-list.html +34 -0
- package/snippets/stock.html +20 -0
- package/snippets/table-actions.html +5 -5
- package/snippets/table.html +2 -2
- package/snippets/titlebar.html +1 -1
- package/snippets/user.html +3 -3
- package/dist/parts/app-column-menu.css +0 -85
- package/snippets/column-menu.html +0 -38
package/dist/components.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* File: oc-theme.css — generated from tokens/semantic.json by @birdycom/omniacss 0.
|
|
1
|
+
/* File: oc-theme.css — generated from tokens/semantic.json by @birdycom/omniacss 0.13.0. Do not edit. */
|
|
2
2
|
/* Semantic theme tokens: every color is light-dark() of an Omnia palette token (oc-colors.css). */
|
|
3
3
|
/* Same source as the desktop export: npx omniacss tokens --format json|dtcg|python */
|
|
4
4
|
:root {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
--oc-foreground: light-dark(var(--color-duskblue-60), var(--color-frostblue-10));
|
|
15
15
|
--oc-muted: light-dark(var(--color-frostblue-60), var(--color-shallowblue-50));
|
|
16
16
|
--oc-muted-foreground: light-dark(var(--color-stormblue-40), var(--color-mistblue-70));
|
|
17
|
+
--oc-subtle-foreground: light-dark(var(--color-stormblue-10), var(--color-mistblue-95));
|
|
17
18
|
--oc-card: light-dark(var(--color-white-50), var(--color-shallowblue-70));
|
|
18
19
|
--oc-popover: light-dark(var(--color-white-50), var(--color-shallowblue-60));
|
|
19
20
|
--oc-popover-foreground: var(--oc-foreground);
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
--oc-transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
59
60
|
--oc-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Cantarell, "Helvetica Neue", sans-serif;
|
|
60
61
|
--oc-font-mono: "Cascadia Code", "JetBrains Mono", Consolas, ui-monospace, monospace;
|
|
62
|
+
--oc-font-display: system-ui, -apple-system, "Segoe UI", Roboto, Cantarell, "Helvetica Neue", sans-serif;
|
|
61
63
|
|
|
62
64
|
/* Shadows (x y blur spread; color + alpha per mode in --oc-shadow-<name>-color) */
|
|
63
65
|
--oc-shadow-sm-color: light-dark(oklch(from var(--color-stormblue-50) l c h / 0.08), oklch(from var(--color-black-50) l c h / 0.4));
|
|
@@ -346,7 +348,8 @@ flex-direction: column;
|
|
|
346
348
|
justify-content: center;
|
|
347
349
|
gap: 8px;
|
|
348
350
|
font-family: inherit;
|
|
349
|
-
|
|
351
|
+
/* --oc-btn-weight: a theme can make every button bolder (600). */
|
|
352
|
+
font-weight: var(--oc-btn-weight, 500);
|
|
350
353
|
font-size: 0.875rem;
|
|
351
354
|
line-height: 1.25rem;
|
|
352
355
|
padding: 0.5rem 1rem;
|
|
@@ -481,19 +484,68 @@ flex-direction: column;
|
|
|
481
484
|
}
|
|
482
485
|
|
|
483
486
|
.oc-btn--lg {
|
|
484
|
-
font-size: 1rem;
|
|
485
|
-
padding: 0.625rem 1.5rem;
|
|
487
|
+
font-size: var(--oc-btn-lg-size, 1rem);
|
|
488
|
+
padding: var(--oc-btn-lg-padding, 0.625rem 1.5rem);
|
|
486
489
|
border-radius: calc(var(--oc-radius) + 2px);
|
|
487
490
|
}
|
|
488
491
|
|
|
492
|
+
/* Square, exactly as tall as a text button of the same size, so both line up in a toolbar. */
|
|
489
493
|
.oc-btn--icon {
|
|
490
|
-
|
|
491
|
-
|
|
494
|
+
--oc-btn-square: calc(2.25rem + 2px);
|
|
495
|
+
padding: 0;
|
|
496
|
+
inline-size: var(--oc-btn-square);
|
|
497
|
+
block-size: var(--oc-btn-square);
|
|
498
|
+
flex-shrink: 0;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.oc-btn--icon.oc-btn--xs { --oc-btn-square: calc(1.75rem + 2px); }
|
|
502
|
+
.oc-btn--icon.oc-btn--sm { --oc-btn-square: calc(2rem + 2px); }
|
|
503
|
+
.oc-btn--icon.oc-btn--lg { --oc-btn-square: calc(2.5rem + 2px); }
|
|
504
|
+
|
|
505
|
+
/* Webicons in a button: <iconify-icon> before (or after) the label, the same size as an inline SVG. */
|
|
506
|
+
.oc-btn > iconify-icon { font-size: 1.15em; flex-shrink: 0; }
|
|
507
|
+
|
|
508
|
+
/* Soft — tinted background in a tone, for row actions (edit, view, delete …) and quiet secondary actions.
|
|
509
|
+
--soft-primary | --soft-success | --soft-warning | --soft-destructive | --soft-info */
|
|
510
|
+
.oc-btn[class*="oc-btn--soft-"] {
|
|
511
|
+
background: light-dark(oklch(from var(--oc-btn-tone) l c h / 0.12), oklch(from var(--oc-btn-tone) l c h / 0.22));
|
|
512
|
+
color: var(--oc-btn-tone);
|
|
513
|
+
border-color: transparent;
|
|
514
|
+
}
|
|
515
|
+
.oc-btn[class*="oc-btn--soft-"]:hover {
|
|
516
|
+
background: light-dark(oklch(from var(--oc-btn-tone) l c h / 0.2), oklch(from var(--oc-btn-tone) l c h / 0.32));
|
|
517
|
+
opacity: 1;
|
|
518
|
+
}
|
|
519
|
+
.oc-btn--soft-primary { --oc-btn-tone: var(--oc-primary); }
|
|
520
|
+
.oc-btn--soft-success { --oc-btn-tone: var(--oc-success); }
|
|
521
|
+
.oc-btn--soft-warning { --oc-btn-tone: var(--oc-warning); }
|
|
522
|
+
.oc-btn--soft-destructive { --oc-btn-tone: var(--oc-destructive); }
|
|
523
|
+
.oc-btn--soft-info { --oc-btn-tone: var(--oc-info, light-dark(var(--color-deepskyblue-70), var(--color-skyblue-50))); }
|
|
524
|
+
|
|
525
|
+
/* Plain — only the icon: no surface, no padding; muted until hovered (menus, toolbars, inline actions). */
|
|
526
|
+
.oc-btn--plain {
|
|
527
|
+
padding: 0;
|
|
528
|
+
background: transparent;
|
|
529
|
+
border-color: transparent;
|
|
530
|
+
color: var(--oc-muted-foreground);
|
|
492
531
|
}
|
|
532
|
+
.oc-btn--plain:hover,
|
|
533
|
+
.oc-btn--plain[aria-expanded="true"] { color: var(--oc-primary); opacity: 1; }
|
|
493
534
|
|
|
494
|
-
|
|
495
|
-
.oc-btn
|
|
496
|
-
.oc-btn
|
|
535
|
+
/* Loading — aria-busy="true": a spinner in front of the label (it replaces a leading icon); not clickable. */
|
|
536
|
+
.oc-btn[aria-busy="true"] { pointer-events: none; }
|
|
537
|
+
.oc-btn[aria-busy="true"]::before {
|
|
538
|
+
content: "";
|
|
539
|
+
inline-size: 1em;
|
|
540
|
+
block-size: 1em;
|
|
541
|
+
flex-shrink: 0;
|
|
542
|
+
border: 2px solid currentColor;
|
|
543
|
+
border-inline-end-color: transparent;
|
|
544
|
+
border-radius: 50%;
|
|
545
|
+
animation: oc-btn-spin 0.7s linear infinite;
|
|
546
|
+
}
|
|
547
|
+
.oc-btn[aria-busy="true"] > :is(svg, iconify-icon):first-child { display: none; }
|
|
548
|
+
@keyframes oc-btn-spin { to { rotate: 360deg; } }
|
|
497
549
|
|
|
498
550
|
/* Button group */
|
|
499
551
|
.oc-btn-group {
|
|
@@ -661,8 +713,10 @@ flex-direction: column;
|
|
|
661
713
|
}
|
|
662
714
|
|
|
663
715
|
.oc-field-error {
|
|
716
|
+
margin: 0;
|
|
664
717
|
font-size: 0.8rem;
|
|
665
|
-
|
|
718
|
+
/* In dark mode lifted to at least OKLCH L 0.78, so the message stays readable on a dark card. */
|
|
719
|
+
color: light-dark(var(--oc-destructive), oklch(from var(--oc-destructive) max(l, 0.78) c h));
|
|
666
720
|
}
|
|
667
721
|
|
|
668
722
|
/* Field group — wraps multiple fields with consistent spacing */
|
|
@@ -675,17 +729,23 @@ flex-direction: column;
|
|
|
675
729
|
grid-template-columns: 1fr 1fr;
|
|
676
730
|
}
|
|
677
731
|
|
|
678
|
-
/*
|
|
732
|
+
/* Narrow screens: the fields of a row stack. */
|
|
733
|
+
@media (max-width: 560px) {
|
|
734
|
+
.oc-field-group--row { grid-template-columns: 1fr; }
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/* Text input. A theme or a form variant (.oc-form--filled) can set the field tokens: --oc-input-bg, --oc-input-border,
|
|
738
|
+
--oc-input-padding, --oc-input-radius, --oc-input-size, --oc-ring-width and --oc-ring-alpha (focus ring). */
|
|
679
739
|
.oc-input {
|
|
680
740
|
display: block;
|
|
681
741
|
width: 100%;
|
|
682
742
|
font-family: inherit;
|
|
683
|
-
font-size: 0.875rem;
|
|
743
|
+
font-size: var(--oc-input-size, 0.875rem);
|
|
684
744
|
line-height: 1.5;
|
|
685
|
-
padding: 0.5rem 0.75rem;
|
|
686
|
-
border: 1px solid var(--oc-input);
|
|
687
|
-
border-radius: var(--oc-radius);
|
|
688
|
-
background: transparent;
|
|
745
|
+
padding: var(--oc-input-padding, 0.5rem 0.75rem);
|
|
746
|
+
border: 1px solid var(--oc-input-border, var(--oc-input));
|
|
747
|
+
border-radius: var(--oc-input-radius, var(--oc-radius));
|
|
748
|
+
background: var(--oc-input-bg, transparent);
|
|
689
749
|
color: var(--oc-foreground);
|
|
690
750
|
transition: border-color var(--oc-transition-fast), box-shadow var(--oc-transition-fast);
|
|
691
751
|
outline: none;
|
|
@@ -697,7 +757,7 @@ flex-direction: column;
|
|
|
697
757
|
|
|
698
758
|
.oc-input:focus {
|
|
699
759
|
border-color: var(--oc-ring);
|
|
700
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--oc-ring) 25
|
|
760
|
+
box-shadow: 0 0 0 var(--oc-ring-width, 2px) color-mix(in srgb, var(--oc-ring) var(--oc-ring-alpha, 25%), transparent);
|
|
701
761
|
}
|
|
702
762
|
|
|
703
763
|
.oc-input:disabled {
|
|
@@ -713,12 +773,13 @@ flex-direction: column;
|
|
|
713
773
|
|
|
714
774
|
.oc-input[aria-invalid="true"]:focus,
|
|
715
775
|
.oc-input--error:focus {
|
|
716
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--oc-destructive) 25
|
|
776
|
+
box-shadow: 0 0 0 var(--oc-ring-width, 2px) color-mix(in srgb, var(--oc-destructive) var(--oc-ring-alpha, 25%), transparent);
|
|
717
777
|
}
|
|
718
778
|
|
|
719
779
|
/* Textarea — same base as input */
|
|
720
|
-
textarea.oc-input
|
|
721
|
-
|
|
780
|
+
textarea.oc-input,
|
|
781
|
+
.oc-textarea {
|
|
782
|
+
min-height: var(--oc-textarea-min, 80px);
|
|
722
783
|
resize: vertical;
|
|
723
784
|
}
|
|
724
785
|
|
|
@@ -762,12 +823,13 @@ textarea.oc-input {
|
|
|
762
823
|
display: block;
|
|
763
824
|
width: 100%;
|
|
764
825
|
font-family: inherit;
|
|
765
|
-
font-size: 0.875rem;
|
|
826
|
+
font-size: var(--oc-input-size, 0.875rem);
|
|
766
827
|
line-height: 1.5;
|
|
767
|
-
padding:
|
|
768
|
-
|
|
769
|
-
border
|
|
770
|
-
|
|
828
|
+
padding: var(--oc-input-padding, 0.5rem 0.75rem);
|
|
829
|
+
padding-inline-end: 2rem;
|
|
830
|
+
border: 1px solid var(--oc-input-border, var(--oc-input));
|
|
831
|
+
border-radius: var(--oc-input-radius, var(--oc-radius));
|
|
832
|
+
background-color: var(--oc-input-bg, transparent);
|
|
771
833
|
color: var(--oc-foreground);
|
|
772
834
|
appearance: none;
|
|
773
835
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2371717a' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
|
|
@@ -781,8 +843,9 @@ textarea.oc-input {
|
|
|
781
843
|
|
|
782
844
|
.oc-select:focus {
|
|
783
845
|
border-color: var(--oc-ring);
|
|
784
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--oc-ring) 25
|
|
846
|
+
box-shadow: 0 0 0 var(--oc-ring-width, 2px) color-mix(in srgb, var(--oc-ring) var(--oc-ring-alpha, 25%), transparent);
|
|
785
847
|
}
|
|
848
|
+
.oc-select[aria-invalid="true"] { border-color: var(--oc-destructive); }
|
|
786
849
|
|
|
787
850
|
/* Select option dark theme — prevents white background in dropdowns */
|
|
788
851
|
.oc-select option {
|
|
@@ -1550,7 +1613,7 @@ textarea.oc-input {
|
|
|
1550
1613
|
border: none;
|
|
1551
1614
|
height: 1px;
|
|
1552
1615
|
background: var(--oc-border);
|
|
1553
|
-
margin: 16px 0;
|
|
1616
|
+
margin: var(--oc-separator-space, 16px) 0;
|
|
1554
1617
|
}
|
|
1555
1618
|
|
|
1556
1619
|
.oc-separator--vertical {
|
|
@@ -1633,6 +1696,8 @@ textarea.oc-input {
|
|
|
1633
1696
|
/* Vertical tabs */
|
|
1634
1697
|
.oc-tab-list--vertical {
|
|
1635
1698
|
flex-direction: column;
|
|
1699
|
+
/* Every tab as wide as the list, so the active line on the right stays on one vertical line. */
|
|
1700
|
+
align-items: stretch;
|
|
1636
1701
|
border-bottom: none;
|
|
1637
1702
|
border-right: 1px solid var(--oc-border);
|
|
1638
1703
|
}
|
|
@@ -1645,7 +1710,9 @@ textarea.oc-input {
|
|
|
1645
1710
|
text-align: left;
|
|
1646
1711
|
}
|
|
1647
1712
|
|
|
1648
|
-
.oc-tab-list--vertical .oc-tab-trigger.is-active
|
|
1713
|
+
.oc-tab-list--vertical .oc-tab-trigger.is-active,
|
|
1714
|
+
.oc-tab-list--vertical .oc-tab-trigger[aria-selected="true"] {
|
|
1715
|
+
border-bottom-color: transparent;
|
|
1649
1716
|
border-right-color: var(--oc-foreground);
|
|
1650
1717
|
}
|
|
1651
1718
|
|
|
@@ -1848,6 +1915,12 @@ textarea.oc-input {
|
|
|
1848
1915
|
border-radius: calc(var(--oc-radius) + 2px);
|
|
1849
1916
|
}
|
|
1850
1917
|
.oc-table-wrap > .oc-table { min-inline-size: 32rem; }
|
|
1918
|
+
/* The rounded frame draws the outer border; the cells keep only the inner lines, so nothing is cut at the corners. */
|
|
1919
|
+
.oc-table-wrap:has(> .oc-table) { border: 1px solid var(--oc-border); }
|
|
1920
|
+
.oc-table-wrap > .oc-table :is(th, td):first-child { border-inline-start: 0; }
|
|
1921
|
+
.oc-table-wrap > .oc-table :is(th, td):last-child { border-inline-end: 0; }
|
|
1922
|
+
.oc-table-wrap > .oc-table > :first-child > tr:first-child > * { border-block-start: 0; }
|
|
1923
|
+
.oc-table-wrap > .oc-table > :last-child > tr:last-child > * { border-block-end: 0; }
|
|
1851
1924
|
|
|
1852
1925
|
/* ===================================================================
|
|
1853
1926
|
Shared docs utility table styles (dark theme)
|
|
@@ -1984,6 +2057,42 @@ a.oc-breadcrumb-item:hover {
|
|
|
1984
2057
|
font-weight: 500;
|
|
1985
2058
|
}
|
|
1986
2059
|
|
|
2060
|
+
/* Icons: a webicon or SVG before the label of any item (the link or the item itself). */
|
|
2061
|
+
.oc-breadcrumb-item,
|
|
2062
|
+
.oc-breadcrumb-item a { gap: 0.375rem; }
|
|
2063
|
+
.oc-breadcrumb-item a { display: inline-flex; align-items: center; }
|
|
2064
|
+
.oc-breadcrumb-item :is(svg, iconify-icon) { font-size: 1.1em; inline-size: 1.1em; block-size: 1.1em; flex-shrink: 0; }
|
|
2065
|
+
|
|
2066
|
+
/* --chevron: a › chevron between the items instead of "/". */
|
|
2067
|
+
.oc-breadcrumb--chevron .oc-breadcrumb-item + .oc-breadcrumb-item::before {
|
|
2068
|
+
content: "";
|
|
2069
|
+
inline-size: 1em;
|
|
2070
|
+
block-size: 1em;
|
|
2071
|
+
margin: 0 0.25rem;
|
|
2072
|
+
background: currentColor;
|
|
2073
|
+
color: var(--oc-muted-foreground);
|
|
2074
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8.59 16.58L13.17 12L8.59 7.41L10 6l6 6l-6 6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
2075
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8.59 16.58L13.17 12L8.59 7.41L10 6l6 6l-6 6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/* Collapsed middle of a long trail: a "…" button (opens a menu with the hidden levels). */
|
|
2079
|
+
.oc-breadcrumb-ellipsis {
|
|
2080
|
+
display: inline-flex;
|
|
2081
|
+
align-items: center;
|
|
2082
|
+
justify-content: center;
|
|
2083
|
+
inline-size: 1.75rem;
|
|
2084
|
+
block-size: 1.5rem;
|
|
2085
|
+
padding: 0;
|
|
2086
|
+
border: 0;
|
|
2087
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
2088
|
+
background: transparent;
|
|
2089
|
+
color: var(--oc-muted-foreground);
|
|
2090
|
+
font: inherit;
|
|
2091
|
+
cursor: pointer;
|
|
2092
|
+
}
|
|
2093
|
+
.oc-breadcrumb-ellipsis:hover { background: var(--oc-accent); color: var(--oc-foreground); }
|
|
2094
|
+
.oc-breadcrumb-ellipsis:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
2095
|
+
|
|
1987
2096
|
/* ===================================================================
|
|
1988
2097
|
PROGRESS BAR
|
|
1989
2098
|
=================================================================== */
|
|
@@ -3670,6 +3779,9 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3670
3779
|
background: var(--oc-gradient-brand);
|
|
3671
3780
|
-webkit-background-clip: text;
|
|
3672
3781
|
background-clip: text;
|
|
3782
|
+
/* The gradient only paints the box: keep italic or tight last letters inside it without moving the text. */
|
|
3783
|
+
padding-inline-end: 0.08em;
|
|
3784
|
+
margin-inline-end: -0.08em;
|
|
3673
3785
|
color: transparent;
|
|
3674
3786
|
}
|
|
3675
3787
|
.oc-text-accent { background-image: linear-gradient(100deg, var(--oc-accent-from), var(--oc-accent-to)); }
|
|
@@ -3691,7 +3803,8 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3691
3803
|
/* border-box: the gradient also runs under the transparent 1px border of .oc-btn; from the padding box it would
|
|
3692
3804
|
repeat in that border and show a thin stripe of the other end's color on both sides. */
|
|
3693
3805
|
background: linear-gradient(100deg, var(--oc-accent-from), var(--oc-accent-to)) border-box;
|
|
3694
|
-
|
|
3806
|
+
/* --oc-btn-gradient-ink: dark text on a light accent (set it with the accent). */
|
|
3807
|
+
color: var(--oc-btn-gradient-ink, var(--color-white-50));
|
|
3695
3808
|
box-shadow: 0 10px 30px -10px var(--oc-accent-to);
|
|
3696
3809
|
transition: transform 0.2s var(--oc-ease-out), box-shadow 0.2s;
|
|
3697
3810
|
}
|
|
@@ -3718,7 +3831,8 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3718
3831
|
|
|
3719
3832
|
/* Backdrop: slowly drifting color blobs + fine grid behind the page.
|
|
3720
3833
|
<div class="oc-backdrop" aria-hidden="true"><i class="oc-backdrop__blob"></i><i class="oc-backdrop__grid"></i></div>
|
|
3721
|
-
.oc-backdrop--local keeps it inside a positioned section instead of the viewport.
|
|
3834
|
+
.oc-backdrop--local keeps it inside a positioned section instead of the viewport.
|
|
3835
|
+
A theme sets the colors: --oc-backdrop-1, -2, -3 (the blobs), --oc-backdrop-3-opacity and --oc-backdrop-grid (the lines). */
|
|
3722
3836
|
.oc-backdrop {
|
|
3723
3837
|
position: fixed;
|
|
3724
3838
|
inset: 0;
|
|
@@ -3739,24 +3853,27 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3739
3853
|
opacity: var(--oc-backdrop-opacity, 0.35);
|
|
3740
3854
|
animation: oc-drift 24s ease-in-out infinite alternate;
|
|
3741
3855
|
}
|
|
3742
|
-
.oc-backdrop::before { background: var(--color-blue-40); inset-block-start: -18rem; inset-inline-start: -12rem; }
|
|
3743
|
-
.oc-backdrop::after { background: var(--color-purple-30); inset-block-start: 10rem; inset-inline-end: -16rem; animation-delay: -8s; }
|
|
3744
|
-
.oc-backdrop > .oc-backdrop__blob { background: var(--color-teal-40); inset-block-end: -20rem; inset-inline-start: 30%; animation-delay: -16s; opacity: calc(var(--oc-backdrop-opacity, 0.35) * 0.6); }
|
|
3856
|
+
.oc-backdrop::before { background: var(--oc-backdrop-1, var(--color-blue-40)); inset-block-start: -18rem; inset-inline-start: -12rem; }
|
|
3857
|
+
.oc-backdrop::after { background: var(--oc-backdrop-2, var(--color-purple-30)); inset-block-start: 10rem; inset-inline-end: -16rem; animation-delay: -8s; }
|
|
3858
|
+
.oc-backdrop > .oc-backdrop__blob { background: var(--oc-backdrop-3, var(--color-teal-40)); inset-block-end: -20rem; inset-inline-start: 30%; animation-delay: -16s; opacity: var(--oc-backdrop-3-opacity, calc(var(--oc-backdrop-opacity, 0.35) * 0.6)); }
|
|
3745
3859
|
.oc-backdrop > .oc-backdrop__grid {
|
|
3746
3860
|
position: absolute;
|
|
3747
3861
|
inset: 0;
|
|
3748
3862
|
background-image:
|
|
3749
|
-
linear-gradient(oklch(from var(--oc-foreground) l c h / 0.05) 1px, transparent 1px),
|
|
3750
|
-
linear-gradient(90deg, oklch(from var(--oc-foreground) l c h / 0.05) 1px, transparent 1px);
|
|
3863
|
+
linear-gradient(var(--oc-backdrop-grid, oklch(from var(--oc-foreground) l c h / 0.05)) 1px, transparent 1px),
|
|
3864
|
+
linear-gradient(90deg, var(--oc-backdrop-grid, oklch(from var(--oc-foreground) l c h / 0.05)) 1px, transparent 1px);
|
|
3751
3865
|
background-size: 64px 64px;
|
|
3752
3866
|
mask-image: radial-gradient(ellipse at 50% 0%, black 20%, transparent 70%);
|
|
3753
3867
|
}
|
|
3754
3868
|
@keyframes oc-drift { to { transform: translate(6rem, 4rem) scale(1.15); } }
|
|
3755
3869
|
|
|
3756
3870
|
/* Container, section spacing and display headings */
|
|
3757
|
-
|
|
3871
|
+
/* --oc-container is the width, --oc-container-gutter the room on each side on a narrower screen (1.25rem). */
|
|
3872
|
+
.oc-container { inline-size: min(100% - 2 * var(--oc-container-gutter, 1.25rem), var(--oc-container, 1200px)); margin-inline: auto; }
|
|
3758
3873
|
.oc-section-pad { padding-block: clamp(4rem, 9vw, 7rem); }
|
|
3759
3874
|
.oc-section-pad--tight { padding-block: clamp(2rem, 5vw, 3.5rem); }
|
|
3875
|
+
/* The hero of a home page: a little less room on top and below than a section. */
|
|
3876
|
+
.oc-section-pad--hero { padding-block: clamp(3.5rem, 9vw, 7rem) clamp(3rem, 6vw, 5rem); }
|
|
3760
3877
|
.oc-display {
|
|
3761
3878
|
margin: 0 0 1.5rem;
|
|
3762
3879
|
font-size: clamp(2.6rem, 5.6vw, 4.6rem);
|
|
@@ -3766,20 +3883,67 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3766
3883
|
text-wrap: balance;
|
|
3767
3884
|
}
|
|
3768
3885
|
.oc-display--md { font-size: clamp(1.9rem, 4vw, 3rem); }
|
|
3886
|
+
.oc-display--sm { font-size: clamp(1.5rem, 3vw, 2.1rem); }
|
|
3769
3887
|
.oc-lead { margin: 0 0 1rem; color: var(--oc-muted-foreground); font-size: 1.15rem; line-height: 1.65; max-inline-size: 42rem; text-wrap: pretty; }
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3888
|
+
/* Larger and narrower, for a home hero. */
|
|
3889
|
+
.oc-lead--lg { font-size: 1.2rem; max-inline-size: 36rem; }
|
|
3890
|
+
.oc-split { display: grid; grid-template-columns: var(--oc-split-cols, repeat(auto-fit, minmax(min(100%, 24rem), 1fr))); gap: var(--oc-split-gap, 3rem); align-items: center; }
|
|
3891
|
+
/* Ratio presets (one column up to 960px): --lead makes the text column wider, --media the image column.
|
|
3892
|
+
Your own ratio: set --oc-split-cols in a class (and to 1fr in a media query for small screens). */
|
|
3893
|
+
.oc-split--lead { --oc-split-cols: 1.25fr 1fr; }
|
|
3894
|
+
.oc-split--media { --oc-split-cols: 1fr 1.15fr; }
|
|
3895
|
+
.oc-split__media { display: grid; place-items: center; }
|
|
3896
|
+
/* Columns aligned at the top (a fact list next to a long text). */
|
|
3897
|
+
.oc-split--top { align-items: start; }
|
|
3898
|
+
/* --oc-split-cols-wide: your own ratio that only applies above 960px, so the split still stacks on a phone. */
|
|
3899
|
+
@media (min-width: 961px) {
|
|
3900
|
+
.oc-split { grid-template-columns: var(--oc-split-cols-wide, var(--oc-split-cols, repeat(auto-fit, minmax(min(100%, 24rem), 1fr)))); }
|
|
3901
|
+
}
|
|
3902
|
+
@media (max-width: 960px) {
|
|
3903
|
+
.oc-split--lead, .oc-split--media { --oc-split-cols: 1fr; }
|
|
3904
|
+
/* The image (a product mark, a screenshot) comes first on a small screen, aligned at the start, and an xl tile shrinks. */
|
|
3905
|
+
.oc-split__media { order: -1; justify-items: start; }
|
|
3906
|
+
.oc-split__media .oc-icon-tile--xl { --oc-tile-size: 7rem; }
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
/* Code window: editor frame with traffic lights and a file name. It follows the page theme: a light frame and light
|
|
3910
|
+
syntax colors in light mode, the Omnia Dark editor colors in dark mode. --dark keeps it dark on a light page (the
|
|
3911
|
+
hero blocks, where it reads as an editor screenshot). */
|
|
3773
3912
|
.oc-code-window {
|
|
3774
3913
|
position: relative;
|
|
3775
3914
|
border: 1px solid var(--oc-border-strong);
|
|
3776
3915
|
border-radius: var(--oc-radius-lg);
|
|
3777
|
-
background: oklch(from var(--color-shallowblue-60) l c h / 0.9);
|
|
3916
|
+
background: light-dark(var(--oc-card), oklch(from var(--color-shallowblue-60) l c h / 0.9));
|
|
3778
3917
|
box-shadow: 0 40px 80px -30px oklch(from var(--color-black-50) l c h / 0.6);
|
|
3779
3918
|
backdrop-filter: blur(12px);
|
|
3780
3919
|
overflow: hidden;
|
|
3781
|
-
|
|
3782
|
-
|
|
3920
|
+
/* Syntax colors (oc-code-syntax.css) for both themes; the code sits on the window surface. */
|
|
3921
|
+
--oc-syntax-bg: transparent;
|
|
3922
|
+
--oc-syntax-bg-preview: transparent;
|
|
3923
|
+
--oc-syntax-fg: light-dark(var(--color-shallowblue-60), var(--color-silver-60));
|
|
3924
|
+
--oc-syntax-comment: light-dark(var(--color-gray-60), var(--color-purple-2));
|
|
3925
|
+
--oc-syntax-string: light-dark(var(--color-green-80), var(--color-green-2));
|
|
3926
|
+
--oc-syntax-keyword: light-dark(var(--color-purple-70), var(--color-purple-2));
|
|
3927
|
+
--oc-syntax-variable: light-dark(var(--color-skycyan-90), var(--color-skycyan-30));
|
|
3928
|
+
--oc-syntax-function: light-dark(var(--color-skycyan-90), var(--color-skycyan-60));
|
|
3929
|
+
--oc-syntax-number: light-dark(var(--color-peru-80), var(--color-peru-40));
|
|
3930
|
+
--oc-syntax-operator: light-dark(var(--color-red-70), var(--color-red-50));
|
|
3931
|
+
--oc-syntax-constant: light-dark(var(--color-skycyan-90), var(--color-skycyan-70));
|
|
3932
|
+
--oc-syntax-storage: light-dark(var(--color-green-80), var(--color-green-2));
|
|
3933
|
+
--oc-syntax-tag: light-dark(var(--color-brown-70), var(--color-brown-10));
|
|
3934
|
+
--oc-syntax-attr: light-dark(var(--color-skycyan-90), var(--color-skycyan-70));
|
|
3935
|
+
--oc-syntax-prop: light-dark(var(--color-green-80), var(--color-green-2));
|
|
3936
|
+
--oc-syntax-value: light-dark(var(--color-teal-80), var(--color-teal-10));
|
|
3937
|
+
--oc-syntax-cls: light-dark(var(--color-darkgoldenrod-80), var(--color-wheat-70));
|
|
3938
|
+
--oc-syntax-id: light-dark(var(--color-skycyan-90), var(--color-skycyan-70));
|
|
3939
|
+
--oc-syntax-unit: light-dark(var(--color-peru-80), var(--color-peru-40));
|
|
3940
|
+
--oc-syntax-key: light-dark(var(--color-brown-70), var(--color-brown-10));
|
|
3941
|
+
--oc-code-block-line-number-color: light-dark(var(--color-gray-40), oklch(from var(--color-white-2) l c h / 0.25));
|
|
3942
|
+
}
|
|
3943
|
+
.oc-code-window--dark { color-scheme: dark; box-shadow: 0 40px 80px -30px oklch(from var(--color-black-50) l c h / 0.6); }
|
|
3944
|
+
.oc-code-window:not(.oc-code-window--dark) { box-shadow: 0 24px 60px -30px oklch(from var(--color-black-50) l c h / 0.25); }
|
|
3945
|
+
/* The code block inside follows the window instead of its own always-dark theme. */
|
|
3946
|
+
.oc-code-window :is(.oc-code-block, pre.code-preview) { color-scheme: inherit; }
|
|
3783
3947
|
.oc-code-window__bar { display: flex; align-items: center; gap: 0.45rem; padding: 0.8rem 1rem; border-block-end: 1px solid var(--oc-border); }
|
|
3784
3948
|
.oc-code-window__bar > i { inline-size: 0.7rem; aspect-ratio: 1; border-radius: 50%; background: var(--color-red-50); }
|
|
3785
3949
|
.oc-code-window__bar > i:nth-child(2) { background: var(--color-orange-60); }
|
|
@@ -3828,7 +3992,12 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3828
3992
|
@keyframes oc-float { 50% { translate: 0 -12px; } }
|
|
3829
3993
|
|
|
3830
3994
|
/* Product / feature card with hover lift and accent glow */
|
|
3831
|
-
.oc-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--oc-card-min, 17rem)), 1fr)); gap: 1.25rem; }
|
|
3995
|
+
.oc-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--oc-card-min, 17rem)), 1fr)); gap: var(--oc-card-gap, 1.25rem); }
|
|
3996
|
+
/* Wide cards (projects, case studies): from 32rem, three in a row on a wide screen. */
|
|
3997
|
+
.oc-card-grid--wide { --oc-card-min: 32rem; --oc-card-gap: 1.5rem; }
|
|
3998
|
+
/* --fit: the cards stretch over the full width when there are fewer than fit in a row (auto-fit instead of auto-fill). */
|
|
3999
|
+
.oc-card-grid--fit { grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--oc-card-min, 17rem)), 1fr)); }
|
|
4000
|
+
@media (min-width: 1280px) { .oc-card-grid--wide { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
|
3832
4001
|
.oc-product-card {
|
|
3833
4002
|
position: relative;
|
|
3834
4003
|
display: flex;
|
|
@@ -3842,7 +4011,7 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3842
4011
|
var(--oc-surface);
|
|
3843
4012
|
color: var(--oc-foreground);
|
|
3844
4013
|
text-decoration: none;
|
|
3845
|
-
transition:
|
|
4014
|
+
transition: translate 0.35s var(--oc-ease-out), border-color 0.35s, --oc-glow 0.35s;
|
|
3846
4015
|
}
|
|
3847
4016
|
@property --oc-glow {
|
|
3848
4017
|
syntax: "<number>";
|
|
@@ -3851,7 +4020,8 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3851
4020
|
}
|
|
3852
4021
|
.oc-product-card:hover {
|
|
3853
4022
|
--oc-glow: 0.22;
|
|
3854
|
-
|
|
4023
|
+
/* The lift uses translate; a fly-in (.oc-reveal--fly) animates the transform, so both keep working. */
|
|
4024
|
+
translate: 0 calc(-1 * var(--oc-hover-lift, 6px));
|
|
3855
4025
|
border-color: oklch(from var(--oc-accent-from) l c h / 0.55);
|
|
3856
4026
|
}
|
|
3857
4027
|
.oc-product-card__top { display: flex; justify-content: space-between; align-items: flex-start; margin-block-end: 0.75rem; }
|
|
@@ -3907,9 +4077,17 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3907
4077
|
}
|
|
3908
4078
|
.oc-stat-strip__item { padding: 2rem 1.5rem; border-inline-start: 1px solid var(--oc-border); }
|
|
3909
4079
|
.oc-stat-strip__item:first-child { border-inline-start: 0; }
|
|
3910
|
-
.oc-stat-strip__value { font-size: clamp(2.2rem, 4vw, 3rem); font-weight: 700; line-height: 1.1; }
|
|
4080
|
+
.oc-stat-strip__value { font-family: var(--oc-font-display, inherit); font-size: clamp(2.2rem, 4vw, 3rem); font-weight: 700; line-height: 1.1; }
|
|
3911
4081
|
.oc-stat-strip__suffix { color: var(--oc-brand-1); }
|
|
3912
4082
|
.oc-stat-strip__label { display: block; color: var(--oc-muted-foreground); font-size: 0.92rem; }
|
|
4083
|
+
/* Plain: no lines and no background of its own, for a strip inside an .oc-band (full width) or a card. */
|
|
4084
|
+
.oc-stat-strip--plain { border-block: 0; background: none; }
|
|
4085
|
+
/* Four figures on a narrower screen: two rows of two instead of three and one. */
|
|
4086
|
+
@media (max-width: 960px) {
|
|
4087
|
+
.oc-stat-strip:has(> .oc-stat-strip__item:nth-child(4):last-child) { grid-template-columns: 1fr 1fr; }
|
|
4088
|
+
.oc-stat-strip:has(> .oc-stat-strip__item:nth-child(4):last-child) > .oc-stat-strip__item:nth-child(3) { border-inline-start: 0; }
|
|
4089
|
+
.oc-stat-strip:has(> .oc-stat-strip__item:nth-child(4):last-child) > .oc-stat-strip__item:nth-child(n + 3) { border-block-start: 1px solid var(--oc-border); }
|
|
4090
|
+
}
|
|
3913
4091
|
|
|
3914
4092
|
/* Spotlight: large highlighted panel with copy + module list */
|
|
3915
4093
|
.oc-spotlight {
|
|
@@ -3927,8 +4105,12 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3927
4105
|
}
|
|
3928
4106
|
.oc-spotlight__title { display: flex; align-items: center; gap: 1rem; margin: 1.25rem 0 0.5rem; font-size: clamp(2.2rem, 5vw, 3.5rem); line-height: 1.1; }
|
|
3929
4107
|
.oc-spotlight__tagline { color: light-dark(var(--oc-accent-from), oklch(from var(--oc-accent-from) max(l, 0.72) c h)); font-size: 1.2rem; font-weight: 600; }
|
|
4108
|
+
/* --oc-spotlight-cols: your own ratio above 960px (copy | modules); on a smaller screen the spotlight stacks. */
|
|
4109
|
+
@media (min-width: 961px) {
|
|
4110
|
+
.oc-spotlight { grid-template-columns: var(--oc-spotlight-cols, repeat(auto-fit, minmax(min(100%, 22rem), 1fr))); }
|
|
4111
|
+
}
|
|
3930
4112
|
.oc-spotlight__modules { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 0.75rem; margin: 0; padding: 0; list-style: none; }
|
|
3931
|
-
.oc-spotlight__modules > li { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-surface); font-size: 0.95rem; }
|
|
4113
|
+
.oc-spotlight__modules > li { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-spotlight-module-bg, var(--oc-surface)); font-size: 0.95rem; }
|
|
3932
4114
|
|
|
3933
4115
|
/* Steps: numbered process with a gradient tick (numbers come from a CSS counter) */
|
|
3934
4116
|
.oc-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1.25rem; margin: 0; padding: 0; list-style: none; counter-reset: oc-step; }
|
|
@@ -3961,6 +4143,9 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3961
4143
|
/* Tag list (tech stack) and check list */
|
|
3962
4144
|
.oc-tag-list { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
|
|
3963
4145
|
.oc-tag-list > li { padding: 0.55rem 1.1rem; border: 1px solid oklch(from var(--oc-accent-from) l c h / 0.35); border-radius: 999px; background: oklch(from var(--oc-accent-from) l c h / 0.1); font-family: var(--oc-font-mono); font-size: 0.88rem; }
|
|
4146
|
+
/* Neutral chips (no accent color) and large chips in the display font, e.g. a strip of brand names. */
|
|
4147
|
+
.oc-tag-list--neutral > li { border-color: var(--oc-border); background: var(--oc-surface); color: var(--oc-foreground); }
|
|
4148
|
+
.oc-tag-list--lg > li { padding: 0.45rem 1rem; font-family: var(--oc-font-display, inherit); font-size: 1rem; font-weight: 700; letter-spacing: 0.02em; }
|
|
3964
4149
|
.oc-check-list { display: grid; gap: 0.5rem; margin: 1.1rem 0 0; padding: 0; list-style: none; font-size: 0.93rem; }
|
|
3965
4150
|
.oc-check-list > li { display: flex; gap: 0.5rem; align-items: baseline; }
|
|
3966
4151
|
.oc-check-list > li::before { content: "✓"; color: var(--oc-brand-1); font-weight: 700; }
|
|
@@ -3995,6 +4180,8 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
3995
4180
|
.oc-site-header__nav a:not(.oc-btn):hover,
|
|
3996
4181
|
.oc-site-header__nav a[aria-current="page"] { color: var(--oc-foreground); }
|
|
3997
4182
|
.oc-site-header__toggle { display: none; padding: 0.5rem 0.7rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-surface); color: var(--oc-foreground); cursor: pointer; }
|
|
4183
|
+
/* The icon in the menu button is a block, so the button is as tall as a line of text (no inline gap under it). */
|
|
4184
|
+
.oc-site-header__toggle :is(svg, img, iconify-icon, .oc-icon) { display: block; }
|
|
3998
4185
|
@media (min-width: 861px) {
|
|
3999
4186
|
/* Desktop: the popover nav is always visible in the header bar. */
|
|
4000
4187
|
.oc-site-header__nav[popover] { display: flex; }
|
|
@@ -4019,6 +4206,8 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
4019
4206
|
backdrop-filter: blur(16px);
|
|
4020
4207
|
}
|
|
4021
4208
|
.oc-site-header__nav[popover] a:not(.oc-btn) { padding: 0.75rem 0; font-size: 1.1rem; }
|
|
4209
|
+
/* The call to action (.oc-site-header__cta, a button) gets room above it in the open menu. */
|
|
4210
|
+
.oc-site-header__cta { margin-block-start: 0.75rem; }
|
|
4022
4211
|
}
|
|
4023
4212
|
|
|
4024
4213
|
/* Site footer: brand column + link columns + bottom bar */
|
|
@@ -4026,21 +4215,33 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
4026
4215
|
margin-block-start: 4rem;
|
|
4027
4216
|
padding-block-start: 4rem;
|
|
4028
4217
|
border-block-start: 1px solid var(--oc-border);
|
|
4029
|
-
background: linear-gradient(to bottom, transparent, var(--oc-surface-strong));
|
|
4218
|
+
background: var(--oc-footer-bg, linear-gradient(to bottom, transparent, var(--oc-surface-strong)));
|
|
4030
4219
|
color: var(--oc-muted-foreground);
|
|
4031
4220
|
font-size: 0.95rem;
|
|
4221
|
+
container-type: inline-size;
|
|
4032
4222
|
}
|
|
4033
|
-
|
|
4223
|
+
/* Columns: --oc-footer-cols (about + 3 link columns). Footer narrower than 66rem (about a 1060px screen):
|
|
4224
|
+
--oc-footer-cols-md columns (3) with the about column over the full width; narrower than 54rem: two columns.
|
|
4225
|
+
Container queries on the footer, so a footer in a narrow column or a preview follows its own width. */
|
|
4226
|
+
.oc-site-footer__grid { display: grid; grid-template-columns: var(--oc-footer-cols, 1.6fr repeat(3, 1fr)); gap: 2.5rem; }
|
|
4034
4227
|
.oc-site-footer__about p { margin-block: 1rem 0; max-inline-size: 22rem; }
|
|
4035
4228
|
.oc-site-footer__title { margin: 0 0 1rem; color: var(--oc-foreground); font-size: 0.95rem; font-weight: 700; }
|
|
4036
4229
|
.oc-site-footer__list { display: flex; flex-direction: column; gap: 0.5rem; margin: 0; padding: 0; list-style: none; font-style: normal; }
|
|
4037
4230
|
.oc-site-footer a { color: inherit; text-decoration: none; }
|
|
4038
4231
|
.oc-site-footer a:hover { color: var(--oc-foreground); }
|
|
4039
|
-
.oc-site-footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; margin-block-start: 3rem; padding-block: 1.5rem; border-block-start: 1px solid var(--oc-border); font-size: 0.85rem; }
|
|
4040
|
-
|
|
4041
|
-
|
|
4232
|
+
.oc-site-footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; margin-block-start: 3rem; padding-block: 1.5rem; border-block-start: 1px solid var(--oc-border); color: var(--oc-footer-bottom-color, inherit); font-size: 0.85rem; }
|
|
4233
|
+
/* A second title further down in a column (e.g. "Tools" under the projects). */
|
|
4234
|
+
.oc-site-footer__subtitle { margin-block-start: 1.75rem; }
|
|
4235
|
+
/* Legal links in the bottom bar (Terms · Privacy · Cookies) and the build number after them. */
|
|
4236
|
+
.oc-site-footer__legal { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; margin: 0; padding: 0; list-style: none; }
|
|
4237
|
+
.oc-site-footer__build { opacity: 0.7; font-variant-numeric: tabular-nums; }
|
|
4238
|
+
@container (max-width: 66rem) {
|
|
4239
|
+
.oc-site-footer__grid { grid-template-columns: repeat(var(--oc-footer-cols-md, 3), minmax(0, 1fr)); }
|
|
4042
4240
|
.oc-site-footer__about { grid-column: 1 / -1; }
|
|
4043
4241
|
}
|
|
4242
|
+
@container (max-width: 54rem) {
|
|
4243
|
+
.oc-site-footer__grid { grid-template-columns: 1fr 1fr; }
|
|
4244
|
+
}
|
|
4044
4245
|
|
|
4045
4246
|
/* Reveal on scroll: elements slide in when they enter the viewport (scroll-driven, no JS; visible without support) */
|
|
4046
4247
|
@supports (animation-timeline: view()) {
|
|
@@ -4437,7 +4638,21 @@ details.oc-collapsible[open]::details-content { block-size: auto; }
|
|
|
4437
4638
|
.oc-slider:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px oklch(from var(--oc-ring) l c h / 0.28); }
|
|
4438
4639
|
.oc-slider:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px oklch(from var(--oc-ring) l c h / 0.28); }
|
|
4439
4640
|
.oc-slider:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
4440
|
-
|
|
4641
|
+
/* In a vertical writing mode the inline axis is vertical: inline-size is the height of the slider. */
|
|
4642
|
+
.oc-slider--vertical { writing-mode: vertical-lr; direction: rtl; inline-size: 10rem; block-size: var(--oc-slider-thumb); }
|
|
4643
|
+
/* Scale under a stepped slider: one mark per step (or per labelled value), centered under the thumb positions.
|
|
4644
|
+
The padding keeps the first and last mark under the thumb center at min and max. */
|
|
4645
|
+
.oc-slider-scale {
|
|
4646
|
+
display: flex;
|
|
4647
|
+
justify-content: space-between;
|
|
4648
|
+
padding-inline: calc(var(--oc-slider-thumb, 1.125rem) / 2);
|
|
4649
|
+
margin-block-start: 0.25rem;
|
|
4650
|
+
font-size: 0.6875rem;
|
|
4651
|
+
font-variant-numeric: tabular-nums;
|
|
4652
|
+
color: var(--oc-muted-foreground);
|
|
4653
|
+
}
|
|
4654
|
+
.oc-slider-scale > * { display: flex; flex-direction: column; align-items: center; gap: 0.125rem; inline-size: 0; }
|
|
4655
|
+
.oc-slider-scale > *::before { content: ""; inline-size: 1px; block-size: 0.375rem; background: var(--oc-border); }
|
|
4441
4656
|
.oc-slider--vertical::-webkit-slider-runnable-track {
|
|
4442
4657
|
background: linear-gradient(to top, var(--oc-primary) var(--oc-slider-progress), var(--oc-muted) var(--oc-slider-progress));
|
|
4443
4658
|
}
|
|
@@ -5440,93 +5655,6 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
5440
5655
|
@keyframes oc-busy-in { from { opacity: 0; } }
|
|
5441
5656
|
@media (prefers-reduced-motion: reduce) { .oc-busy-overlay[open], .oc-busy-overlay[open]::backdrop { animation-duration: 0s; } }
|
|
5442
5657
|
|
|
5443
|
-
/* parts/app-column-menu.css */
|
|
5444
|
-
/* File: packages/ui/src/parts/app-column-menu.css | Map: menu panel -> title + list -> item states -> footer -> CSS-only column hiding (scope) */
|
|
5445
|
-
/* Column menu: a popover with one checkbox per table column (column visibility).
|
|
5446
|
-
CSS-only hiding: wrap the menu and the table in .oc-column-menu-scope and give each checkbox
|
|
5447
|
-
data-col="<column number>". An unchecked box hides that column (th and td) through :has(). */
|
|
5448
|
-
|
|
5449
|
-
/* Panel look. Without the popover attribute it is a normal block (e.g. inside a sheet or sidebar). */
|
|
5450
|
-
.oc-column-menu {
|
|
5451
|
-
inline-size: 14rem;
|
|
5452
|
-
max-inline-size: calc(100vw - 1rem);
|
|
5453
|
-
padding: 0.375rem;
|
|
5454
|
-
font-size: 0.875rem;
|
|
5455
|
-
color: var(--oc-popover-foreground);
|
|
5456
|
-
background: var(--oc-popover);
|
|
5457
|
-
border: 1px solid var(--oc-border);
|
|
5458
|
-
border-radius: var(--oc-radius);
|
|
5459
|
-
box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15);
|
|
5460
|
-
}
|
|
5461
|
-
/* As a popover: in the top layer, tethered to its trigger like the dropdown. */
|
|
5462
|
-
.oc-column-menu[popover] {
|
|
5463
|
-
position: fixed;
|
|
5464
|
-
inset: auto;
|
|
5465
|
-
margin: 4px 0 0;
|
|
5466
|
-
position-area: block-end span-inline-start;
|
|
5467
|
-
position-try-fallbacks: flip-block, flip-inline;
|
|
5468
|
-
transition: opacity var(--oc-transition-fast), display var(--oc-transition-fast) allow-discrete, overlay var(--oc-transition-fast) allow-discrete;
|
|
5469
|
-
}
|
|
5470
|
-
.oc-column-menu[popover]:popover-open {
|
|
5471
|
-
display: block;
|
|
5472
|
-
@starting-style { opacity: 0; }
|
|
5473
|
-
}
|
|
5474
|
-
|
|
5475
|
-
/* Small heading above the list ("Columns"). */
|
|
5476
|
-
.oc-column-menu-title {
|
|
5477
|
-
margin: 0;
|
|
5478
|
-
padding: 0.375rem 0.5rem 0.25rem;
|
|
5479
|
-
font-size: 0.75rem;
|
|
5480
|
-
font-weight: 600;
|
|
5481
|
-
color: var(--oc-muted-foreground);
|
|
5482
|
-
}
|
|
5483
|
-
|
|
5484
|
-
/* The list is a <form>, so a native <button type="reset"> restores the default columns. */
|
|
5485
|
-
.oc-column-menu-list { display: grid; gap: 1px; margin: 0; max-block-size: 18rem; overflow-y: auto; }
|
|
5486
|
-
|
|
5487
|
-
/* One row: label around a checkbox and the column name. */
|
|
5488
|
-
.oc-column-menu-item {
|
|
5489
|
-
display: flex;
|
|
5490
|
-
align-items: center;
|
|
5491
|
-
gap: 0.5rem;
|
|
5492
|
-
padding: 0.375rem 0.5rem;
|
|
5493
|
-
border-radius: calc(var(--oc-radius) - 2px);
|
|
5494
|
-
cursor: pointer;
|
|
5495
|
-
user-select: none;
|
|
5496
|
-
}
|
|
5497
|
-
.oc-column-menu-item:hover { background: var(--oc-accent); }
|
|
5498
|
-
.oc-column-menu-item:has(:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
|
|
5499
|
-
.oc-column-menu-item input { margin: 0; accent-color: var(--oc-primary); }
|
|
5500
|
-
/* Locked column (always visible): <input disabled checked>. */
|
|
5501
|
-
.oc-column-menu-item:has(input:disabled) { color: var(--oc-muted-foreground); cursor: default; }
|
|
5502
|
-
.oc-column-menu-item:has(input:disabled):hover { background: none; }
|
|
5503
|
-
|
|
5504
|
-
/* Footer row with "Reset" (type="reset") or other small buttons. */
|
|
5505
|
-
.oc-column-menu-footer {
|
|
5506
|
-
display: flex;
|
|
5507
|
-
justify-content: flex-end;
|
|
5508
|
-
gap: 0.25rem;
|
|
5509
|
-
margin-block-start: 0.375rem;
|
|
5510
|
-
padding-block-start: 0.375rem;
|
|
5511
|
-
border-block-start: 1px solid var(--oc-border);
|
|
5512
|
-
}
|
|
5513
|
-
|
|
5514
|
-
/* ---- CSS-only column hiding (columns 1 to 10) ---- */
|
|
5515
|
-
.oc-column-menu-scope:has([data-col="1"]:not(:checked)) .oc-table :is(th, td):nth-child(1),
|
|
5516
|
-
.oc-column-menu-scope:has([data-col="2"]:not(:checked)) .oc-table :is(th, td):nth-child(2),
|
|
5517
|
-
.oc-column-menu-scope:has([data-col="3"]:not(:checked)) .oc-table :is(th, td):nth-child(3),
|
|
5518
|
-
.oc-column-menu-scope:has([data-col="4"]:not(:checked)) .oc-table :is(th, td):nth-child(4),
|
|
5519
|
-
.oc-column-menu-scope:has([data-col="5"]:not(:checked)) .oc-table :is(th, td):nth-child(5),
|
|
5520
|
-
.oc-column-menu-scope:has([data-col="6"]:not(:checked)) .oc-table :is(th, td):nth-child(6),
|
|
5521
|
-
.oc-column-menu-scope:has([data-col="7"]:not(:checked)) .oc-table :is(th, td):nth-child(7),
|
|
5522
|
-
.oc-column-menu-scope:has([data-col="8"]:not(:checked)) .oc-table :is(th, td):nth-child(8),
|
|
5523
|
-
.oc-column-menu-scope:has([data-col="9"]:not(:checked)) .oc-table :is(th, td):nth-child(9),
|
|
5524
|
-
.oc-column-menu-scope:has([data-col="10"]:not(:checked)) .oc-table :is(th, td):nth-child(10) { display: none; }
|
|
5525
|
-
|
|
5526
|
-
@media (prefers-reduced-motion: reduce) {
|
|
5527
|
-
.oc-column-menu[popover] { transition: none; }
|
|
5528
|
-
}
|
|
5529
|
-
|
|
5530
5658
|
/* parts/app-confirm-dialog.css */
|
|
5531
5659
|
/* File: packages/ui/src/parts/app-confirm-dialog.css | Map: confirm modifier (icon ring) -> close button -> typed confirm field -> confirm lock -> three-button footer -> narrow screens */
|
|
5532
5660
|
/* Confirm dialog: a variant of the alert dialog (dialog.oc-modal + .oc-alert-dialog).
|
|
@@ -5664,9 +5792,213 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
5664
5792
|
}
|
|
5665
5793
|
@media (prefers-reduced-motion: reduce) { .oc-cookie-consent { transition: none; } }
|
|
5666
5794
|
|
|
5795
|
+
/* parts/app-data-table-footer.css */
|
|
5796
|
+
/* File: packages/ui/src/parts/app-data-table-footer.css | Map: footer grid (count, rows per page, pager) -> page buttons -> narrow screens */
|
|
5797
|
+
/* Footer of the list table (app-data-table.css): the count on the left, rows per page in the middle, the pager on the right. */
|
|
5798
|
+
|
|
5799
|
+
/* Footer: count on the left, rows-per-page in the middle, pager on the right. */
|
|
5800
|
+
.oc-data-table-footer {
|
|
5801
|
+
display: grid;
|
|
5802
|
+
grid-template-columns: 1fr auto 1fr;
|
|
5803
|
+
align-items: center;
|
|
5804
|
+
gap: 0.75rem;
|
|
5805
|
+
}
|
|
5806
|
+
.oc-data-table-summary { margin: 0; font-size: 0.8125rem; color: var(--oc-muted-foreground); }
|
|
5807
|
+
.oc-data-table-per-page { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; }
|
|
5808
|
+
.oc-data-table-per-page .oc-select { inline-size: auto; }
|
|
5809
|
+
.oc-data-table-pager { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.25rem; }
|
|
5810
|
+
.oc-data-table-page {
|
|
5811
|
+
min-inline-size: 2rem;
|
|
5812
|
+
padding: 0.25rem 0.45rem;
|
|
5813
|
+
font: inherit;
|
|
5814
|
+
font-size: 0.8125rem;
|
|
5815
|
+
color: var(--oc-foreground);
|
|
5816
|
+
background: var(--oc-card);
|
|
5817
|
+
border: 1px solid var(--oc-border);
|
|
5818
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
5819
|
+
cursor: pointer;
|
|
5820
|
+
}
|
|
5821
|
+
.oc-data-table-page[aria-current="page"] {
|
|
5822
|
+
color: var(--oc-primary);
|
|
5823
|
+
background: oklch(from var(--oc-primary) l c h / 0.1);
|
|
5824
|
+
border-color: oklch(from var(--oc-primary) l c h / 0.35);
|
|
5825
|
+
}
|
|
5826
|
+
.oc-data-table-page:disabled { opacity: 0.4; cursor: default; }
|
|
5827
|
+
.oc-data-table-page:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
5828
|
+
.oc-data-table-ellipsis { padding-inline: 0.25rem; color: var(--oc-muted-foreground); }
|
|
5829
|
+
|
|
5830
|
+
@media (max-width: 40rem) {
|
|
5831
|
+
.oc-data-table-footer { grid-template-columns: 1fr; }
|
|
5832
|
+
.oc-data-table-pager { justify-content: flex-start; }
|
|
5833
|
+
}
|
|
5834
|
+
|
|
5835
|
+
/* parts/app-data-table-rich.css */
|
|
5836
|
+
/* File: packages/ui/src/parts/app-data-table-rich.css | Map: control height -> header (sort icons, columns) -> type tabs -> filter row -> name, truncate and count cells -> card fields */
|
|
5837
|
+
/* The rich relations list on top of app-data-table.css: tabs that filter a column, filter selects with an icon,
|
|
5838
|
+
rich cells and the label/value lines of the card view. Behaviour: data-oc-col-filter in src/data-table.js. */
|
|
5839
|
+
|
|
5840
|
+
/* Every control is 2rem high: search, filter selects, trash pills, icon buttons, rows per page and the pager. */
|
|
5841
|
+
.oc-data-table :is(.oc-data-table-toolbar, .oc-data-table-filters, .oc-data-table-footer) :is(.oc-input, .oc-select) { block-size: 2rem; padding-block: 0; font-size: 0.8125rem; }
|
|
5842
|
+
.oc-data-table .oc-data-table-page { min-block-size: 2rem; }
|
|
5843
|
+
.oc-data-table :is(.oc-data-table-toolbar, .oc-data-table-filters, thead) .oc-btn--icon { --oc-btn-square: 2rem; }
|
|
5844
|
+
.oc-data-table-toolbar .oc-filter-pill {
|
|
5845
|
+
display: inline-flex;
|
|
5846
|
+
align-items: center;
|
|
5847
|
+
block-size: 2rem;
|
|
5848
|
+
padding-inline: 0.625rem;
|
|
5849
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
5850
|
+
}
|
|
5851
|
+
|
|
5852
|
+
/* Header: the sort mark only on the sorted column (and on hover); the actions header holds the column button. */
|
|
5853
|
+
/* The sort mark always keeps its 1rem, so sorting another column never moves the header: empty while unsorted,
|
|
5854
|
+
faint chevrons on hover, the accent sort icon on the sorted column. */
|
|
5855
|
+
:is(.oc-data-table, .oc-block-datatable) th:has(.oc-table-sort) { cursor: pointer; }
|
|
5856
|
+
:is(.oc-data-table, .oc-block-datatable) th .oc-table-sort::after { content: ""; flex-shrink: 0; inline-size: 1rem; block-size: 1rem; opacity: 1; }
|
|
5857
|
+
:is(.oc-data-table, .oc-block-datatable) th:not([aria-sort]):hover .oc-table-sort::after {
|
|
5858
|
+
background: var(--oc-muted-foreground);
|
|
5859
|
+
opacity: 0.6;
|
|
5860
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 18.17L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15M12 5.83L15.17 9l1.41-1.41L12 3L7.41 7.59L8.83 9z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
5861
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 18.17L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15M12 5.83L15.17 9l1.41-1.41L12 3L7.41 7.59L8.83 9z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
5862
|
+
}
|
|
5863
|
+
/* Sorted column: a bold label and a sort icon in the accent color. aria-sort on a header in the HTML
|
|
5864
|
+
is the column sorted when the page opens. */
|
|
5865
|
+
:is(.oc-data-table, .oc-block-datatable) th[aria-sort] .oc-table-sort { color: var(--oc-foreground); }
|
|
5866
|
+
:is(.oc-data-table, .oc-block-datatable) th[aria-sort] .oc-table-sort::after {
|
|
5867
|
+
content: "";
|
|
5868
|
+
inline-size: 1rem;
|
|
5869
|
+
block-size: 1rem;
|
|
5870
|
+
opacity: 1;
|
|
5871
|
+
background: var(--oc-primary);
|
|
5872
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 17h3l-4 4l-4-4h3V3h2M2 17h10v2H2M6 5v2H2V5m0 6h7v2H2z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
5873
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 17h3l-4 4l-4-4h3V3h2M2 17h10v2H2M6 5v2H2V5m0 6h7v2H2z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
5874
|
+
}
|
|
5875
|
+
:is(.oc-data-table, .oc-block-datatable) th[aria-sort="descending"] .oc-table-sort::after {
|
|
5876
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 7h3l-4-4l-4 4h3v14h2M2 17h10v2H2M6 5v2H2V5m0 6h7v2H2z'/%3E%3C/svg%3E");
|
|
5877
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 7h3l-4-4l-4 4h3v14h2M2 17h10v2H2M6 5v2H2V5m0 6h7v2H2z'/%3E%3C/svg%3E");
|
|
5878
|
+
}
|
|
5879
|
+
.oc-data-table th[data-oc-col="actions"] .oc-data-table-th { justify-content: flex-end; gap: 0.5rem; font-weight: 500; color: var(--oc-muted-foreground); }
|
|
5880
|
+
.oc-data-table td[data-oc-col="actions"] { text-align: end; }
|
|
5881
|
+
/* Row actions per status: data-oc-when="active" (edit, view) only on live rows, data-oc-when="deleted" (restore,
|
|
5882
|
+
delete permanently) only on rows in the trash. */
|
|
5883
|
+
.oc-data-table tr[data-oc-status="deleted"] [data-oc-when="active"],
|
|
5884
|
+
.oc-data-table tr:not([data-oc-status="deleted"]) [data-oc-when="deleted"] { display: none; }
|
|
5885
|
+
/* The column chooser in the actions header: a large plain icon (1.75rem), no button frame. */
|
|
5886
|
+
.oc-data-table-columns-btn { font-size: 1.75rem; }
|
|
5887
|
+
.oc-data-table-columns-btn > :is(svg, iconify-icon) { font-size: 1em; inline-size: 1em; block-size: 1em; }
|
|
5888
|
+
|
|
5889
|
+
/* Tabs above the toolbar (All / Customers / Companies …): icon, label and a count; they filter one column. */
|
|
5890
|
+
.oc-data-table-tabs { display: flex; inline-size: 100%; overflow-x: auto; scrollbar-width: none; }
|
|
5891
|
+
.oc-data-table-tabs .oc-tab-trigger { flex-shrink: 0; }
|
|
5892
|
+
.oc-data-table-tabs .oc-tab-trigger { display: inline-flex; align-items: center; gap: 0.4rem; }
|
|
5893
|
+
.oc-data-table-tabs .oc-tab-trigger :is(svg, iconify-icon) { font-size: 1.1em; inline-size: 1.1em; block-size: 1.1em; }
|
|
5894
|
+
.oc-data-table-tabs .oc-tab-trigger[aria-selected="true"] { color: var(--oc-primary); border-bottom-color: var(--oc-primary); }
|
|
5895
|
+
.oc-data-table-tab-count {
|
|
5896
|
+
min-inline-size: 1.25rem;
|
|
5897
|
+
padding: 0 0.375rem;
|
|
5898
|
+
font-size: 0.6875rem;
|
|
5899
|
+
line-height: 1.125rem;
|
|
5900
|
+
text-align: center;
|
|
5901
|
+
color: var(--oc-muted-foreground);
|
|
5902
|
+
background: var(--oc-muted);
|
|
5903
|
+
border-radius: 999px;
|
|
5904
|
+
}
|
|
5905
|
+
.oc-data-table-tabs [aria-selected="true"] .oc-data-table-tab-count { color: var(--oc-primary); background: oklch(from var(--oc-primary) l c h / 0.12); }
|
|
5906
|
+
/* A count in the color of its type (the same tone as the type badge in the table). */
|
|
5907
|
+
.oc-data-table-tab-count:is(.oc-data-table-tab-count--success, .oc-data-table-tab-count--info, .oc-data-table-tab-count--warning) {
|
|
5908
|
+
color: var(--oc-tab-tone);
|
|
5909
|
+
background: oklch(from var(--oc-tab-tone) l c h / 0.12);
|
|
5910
|
+
box-shadow: inset 0 0 0 1px oklch(from var(--oc-tab-tone) l c h / 0.35);
|
|
5911
|
+
}
|
|
5912
|
+
.oc-data-table-tab-count--success { --oc-tab-tone: var(--oc-success); }
|
|
5913
|
+
.oc-data-table-tab-count--info { --oc-tab-tone: var(--oc-info, light-dark(var(--color-deepskyblue-70), var(--color-skyblue-50))); }
|
|
5914
|
+
.oc-data-table-tab-count--warning { --oc-tab-tone: var(--oc-warning); }
|
|
5915
|
+
|
|
5916
|
+
/* Filter row: selects with a leading icon (status, type …) and the column button on the right. */
|
|
5917
|
+
.oc-data-table-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
|
|
5918
|
+
.oc-data-table-filters[hidden] { display: none; }
|
|
5919
|
+
.oc-data-table-filters > :last-child:is(.oc-btn) { margin-inline-start: auto; }
|
|
5920
|
+
.oc-data-table-filter { position: relative; display: inline-flex; align-items: center; }
|
|
5921
|
+
.oc-data-table-filter > :is(svg, iconify-icon) {
|
|
5922
|
+
position: absolute;
|
|
5923
|
+
inset-inline-start: 0.625rem;
|
|
5924
|
+
font-size: 1rem;
|
|
5925
|
+
inline-size: 1rem;
|
|
5926
|
+
block-size: 1rem;
|
|
5927
|
+
color: var(--oc-muted-foreground);
|
|
5928
|
+
pointer-events: none;
|
|
5929
|
+
}
|
|
5930
|
+
.oc-data-table-filter > .oc-select { inline-size: auto; min-inline-size: 11rem; padding-inline-start: 2.125rem; }
|
|
5931
|
+
|
|
5932
|
+
/* Cells. Name: avatar, name with a badge, and a muted second line (VAT number, e-mail …). */
|
|
5933
|
+
.oc-data-table-name { display: flex; align-items: center; gap: 0.625rem; min-inline-size: 0; }
|
|
5934
|
+
.oc-data-table-name .oc-avatar { inline-size: 2.25rem; block-size: 2.25rem; font-size: 0.75rem; }
|
|
5935
|
+
.oc-data-table-name-text { display: grid; min-inline-size: 0; }
|
|
5936
|
+
.oc-data-table-name-line { display: flex; align-items: center; gap: 0.375rem; font-weight: 600; }
|
|
5937
|
+
.oc-data-table-name-sub { font-size: 0.75rem; color: var(--oc-muted-foreground); }
|
|
5938
|
+
/* One line with an ellipsis; put the full text in title="". */
|
|
5939
|
+
.oc-data-table-truncate { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-inline-size: var(--oc-data-table-truncate, 18rem); }
|
|
5940
|
+
/* A count that opens the related list (projects, invoices …); muted while it is 0. */
|
|
5941
|
+
.oc-data-table-count { font-variant-numeric: tabular-nums; }
|
|
5942
|
+
.oc-data-table-count[data-count="0"] { color: var(--oc-muted-foreground); opacity: 0.75; }
|
|
5943
|
+
/* The sort is visible in the header (icon, bold label); the summary line is for screen readers only. */
|
|
5944
|
+
.oc-data-table-sort-summary {
|
|
5945
|
+
position: absolute;
|
|
5946
|
+
inline-size: 1px;
|
|
5947
|
+
block-size: 1px;
|
|
5948
|
+
margin: 0;
|
|
5949
|
+
overflow: hidden;
|
|
5950
|
+
clip-path: inset(50%);
|
|
5951
|
+
white-space: nowrap;
|
|
5952
|
+
}
|
|
5953
|
+
.oc-data-table-file { display: none; }
|
|
5954
|
+
|
|
5955
|
+
/* The other visible columns as label / value lines, and the row actions at the bottom. */
|
|
5956
|
+
.oc-data-table-card-fields { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.25rem 0.75rem; margin: 0; font-size: 0.8125rem; }
|
|
5957
|
+
.oc-data-table-card-fields dt { color: var(--oc-muted-foreground); }
|
|
5958
|
+
.oc-data-table-card-fields dd { margin: 0; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5959
|
+
.oc-data-table-card-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.375rem; margin-block-start: auto; }
|
|
5960
|
+
|
|
5961
|
+
/* Code cells (article numbers, SKUs): monospace and a step smaller. */
|
|
5962
|
+
.oc-data-table-code { font-family: var(--oc-font-mono); font-size: 0.75rem; }
|
|
5963
|
+
|
|
5964
|
+
/* Version 2, ".oc-data-table--bordered": a tinted header row, a line between the columns and the
|
|
5965
|
+
sort chevrons always visible; the filter select has its icon in a zone of its own. The rest is the same table. */
|
|
5966
|
+
.oc-data-table.oc-data-table--bordered .oc-table :is(th, td) { border-inline-end: 1px solid var(--oc-border); }
|
|
5967
|
+
.oc-data-table.oc-data-table--bordered .oc-table :is(th, td):last-child { border-inline-end: 0; }
|
|
5968
|
+
.oc-data-table.oc-data-table--bordered .oc-table thead th {
|
|
5969
|
+
font-weight: 500;
|
|
5970
|
+
background: color-mix(in oklch, var(--oc-card), var(--oc-foreground) 4%);
|
|
5971
|
+
}
|
|
5972
|
+
.oc-data-table.oc-data-table--bordered .oc-table thead th[data-oc-col="actions"] {
|
|
5973
|
+
background: color-mix(in oklch, var(--oc-card), var(--oc-foreground) 4%);
|
|
5974
|
+
}
|
|
5975
|
+
.oc-data-table--bordered td[data-oc-col="actions"] { text-align: center; }
|
|
5976
|
+
.oc-data-table--bordered th[data-oc-col="actions"] .oc-data-table-th { justify-content: center; }
|
|
5977
|
+
.oc-data-table--bordered th:not([aria-sort]) .oc-table-sort::after,
|
|
5978
|
+
.oc-data-table--bordered th:not([aria-sort]):hover .oc-table-sort::after {
|
|
5979
|
+
content: "";
|
|
5980
|
+
inline-size: 0.875rem;
|
|
5981
|
+
block-size: 0.875rem;
|
|
5982
|
+
opacity: 0.5;
|
|
5983
|
+
background: currentColor;
|
|
5984
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 18.17L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15M12 5.83L15.17 9l1.41-1.41L12 3L7.41 7.59L8.83 9z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
5985
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 18.17L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15M12 5.83L15.17 9l1.41-1.41L12 3L7.41 7.59L8.83 9z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
5986
|
+
}
|
|
5987
|
+
.oc-data-table--bordered .oc-data-table-filter::after {
|
|
5988
|
+
content: "";
|
|
5989
|
+
position: absolute;
|
|
5990
|
+
inset-block: 0;
|
|
5991
|
+
inset-inline-start: 2rem;
|
|
5992
|
+
inline-size: 1px;
|
|
5993
|
+
background: var(--oc-border);
|
|
5994
|
+
pointer-events: none;
|
|
5995
|
+
}
|
|
5996
|
+
.oc-data-table--bordered .oc-data-table-filter > .oc-select { padding-inline-start: 2.75rem; }
|
|
5997
|
+
|
|
5667
5998
|
/* parts/app-data-table.css */
|
|
5668
|
-
/* File: packages/ui/src/parts/app-data-table.css | Map: shell -> toolbar -> frame and scroll -> header
|
|
5669
|
-
/* List table
|
|
5999
|
+
/* File: packages/ui/src/parts/app-data-table.css | Map: shell -> toolbar -> frame and scroll -> header label -> select and actions columns -> deleted row -> bulk bar -> card view -> compact density */
|
|
6000
|
+
/* List table for business apps. The frame, toolbar and footer are styling. Tabs, filter row, rich cells and
|
|
6001
|
+
card fields: app-data-table-rich.css; footer and pager: app-data-table-footer.css.
|
|
5670
6002
|
Behaviour (search, sort, pages, resize, drag, trash, bulk) lives in src/data-table.js.
|
|
5671
6003
|
Set --oc-data-table-max on the shell when the body should scroll and the header should stick. */
|
|
5672
6004
|
|
|
@@ -5679,13 +6011,16 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
5679
6011
|
background: var(--oc-card);
|
|
5680
6012
|
border: 1px solid var(--oc-border);
|
|
5681
6013
|
border-radius: var(--oc-radius);
|
|
6014
|
+
/* Full width, and in a grid or flex parent the table scrolls inside the frame instead of stretching the card. */
|
|
6015
|
+
inline-size: 100%;
|
|
6016
|
+
min-inline-size: 0;
|
|
5682
6017
|
}
|
|
5683
6018
|
|
|
5684
|
-
/* Toolbar: title + gear, search, trash pills, table/cards toggle, page actions. */
|
|
6019
|
+
/* Toolbar: title + gear/filter button, search (takes the free width), trash pills, table/cards toggle, page actions. */
|
|
5685
6020
|
.oc-data-table-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
|
|
5686
6021
|
.oc-data-table-heading { display: flex; align-items: center; gap: 0.5rem; min-inline-size: 0; }
|
|
5687
6022
|
.oc-data-table-title { font-size: 0.875rem; font-weight: 600; }
|
|
5688
|
-
.oc-data-table-search { flex: 1 1
|
|
6023
|
+
.oc-data-table-search { flex: 1 1 10rem; min-inline-size: 9rem; }
|
|
5689
6024
|
.oc-data-table-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-inline-start: auto; }
|
|
5690
6025
|
.oc-data-table-view { display: inline-flex; gap: 0.25rem; }
|
|
5691
6026
|
.oc-data-table-view [aria-pressed="true"] {
|
|
@@ -5693,55 +6028,52 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
5693
6028
|
background: oklch(from var(--oc-primary) l c h / 0.1);
|
|
5694
6029
|
border-color: oklch(from var(--oc-primary) l c h / 0.35);
|
|
5695
6030
|
}
|
|
5696
|
-
|
|
5697
|
-
.oc-data-table-
|
|
5698
|
-
.oc-data-table-
|
|
6031
|
+
/* In the toolbar the trash pills read as one switch: the chosen one is filled. */
|
|
6032
|
+
.oc-data-table-toolbar .oc-filter-pills { border-inline-start: 0; padding-inline-start: 0; }
|
|
6033
|
+
.oc-data-table-toolbar .oc-filter-pill[aria-current="true"] {
|
|
6034
|
+
color: var(--oc-primary-foreground);
|
|
6035
|
+
background: var(--oc-primary);
|
|
6036
|
+
border-color: var(--oc-primary);
|
|
6037
|
+
}
|
|
5699
6038
|
|
|
5700
6039
|
/* The table sits in its own rounded frame. The scroll box keeps the header sticky. */
|
|
5701
6040
|
.oc-data-table-frame {
|
|
6041
|
+
min-inline-size: 0;
|
|
5702
6042
|
overflow: hidden;
|
|
5703
|
-
background: var(--oc-
|
|
6043
|
+
background: var(--oc-card);
|
|
5704
6044
|
border: 1px solid var(--oc-border);
|
|
5705
6045
|
border-radius: calc(var(--oc-radius) - 2px);
|
|
5706
6046
|
}
|
|
5707
6047
|
.oc-data-table-scroll { overflow: auto; max-block-size: var(--oc-data-table-max, none); }
|
|
5708
6048
|
.oc-data-table .oc-table :is(th, td) { border-inline: 0; border-block-start: 0; }
|
|
6049
|
+
/* Columns size to their content and cells stay on one line: a narrow screen scrolls the frame sideways instead of
|
|
6050
|
+
squeezing names and phone numbers onto two lines. .oc-data-table-wrap on a cell lets long text wrap again. */
|
|
6051
|
+
.oc-data-table .oc-data-table-table { table-layout: auto; }
|
|
6052
|
+
.oc-data-table .oc-data-table-table :is(th, td) { white-space: nowrap; vertical-align: middle; padding-inline: 0.625rem; }
|
|
6053
|
+
.oc-data-table .oc-data-table-table td.oc-data-table-wrap { white-space: normal; }
|
|
5709
6054
|
.oc-data-table .oc-table thead th {
|
|
5710
6055
|
position: sticky;
|
|
5711
6056
|
inset-block-start: 0;
|
|
5712
6057
|
z-index: 1;
|
|
5713
|
-
block-size: 2.
|
|
5714
|
-
font-size: 0.
|
|
5715
|
-
font-weight:
|
|
6058
|
+
block-size: 2.75rem;
|
|
6059
|
+
font-size: 0.8125rem;
|
|
6060
|
+
font-weight: 600;
|
|
6061
|
+
color: var(--oc-foreground);
|
|
5716
6062
|
background: var(--oc-card);
|
|
5717
6063
|
}
|
|
5718
|
-
|
|
6064
|
+
/* Striped rows (every second visible row), the row under the pointer a step stronger. Solid colors, so the sticky
|
|
6065
|
+
actions cell can take the same background (inherit) and nothing scrolls through it. */
|
|
6066
|
+
.oc-data-table {
|
|
6067
|
+
--oc-data-table-stripe: color-mix(in oklch, var(--oc-card), var(--oc-foreground) 3.5%);
|
|
6068
|
+
--oc-data-table-hover: color-mix(in oklch, var(--oc-card), var(--oc-primary) 8%);
|
|
6069
|
+
}
|
|
6070
|
+
.oc-data-table .oc-table tbody tr:nth-child(even of :not([hidden])) { background: var(--oc-data-table-stripe); }
|
|
6071
|
+
.oc-data-table .oc-table tbody tr:hover { background: var(--oc-data-table-hover); }
|
|
6072
|
+
.oc-data-table--plain .oc-table tbody tr:nth-child(even of :not([hidden])):not(:hover) { background: none; }
|
|
5719
6073
|
.oc-data-table .oc-table tbody tr:last-child > td { border-block-end: 0; }
|
|
5720
6074
|
|
|
5721
|
-
/* Header label
|
|
5722
|
-
.oc-data-table-th { display: flex; align-items: center; gap: 0.25rem;
|
|
5723
|
-
.oc-data-table-resize {
|
|
5724
|
-
position: absolute;
|
|
5725
|
-
inset-block: 0;
|
|
5726
|
-
inset-inline-end: -0.45rem;
|
|
5727
|
-
inline-size: 0.55rem;
|
|
5728
|
-
padding: 0;
|
|
5729
|
-
background: none;
|
|
5730
|
-
border: 0;
|
|
5731
|
-
cursor: col-resize;
|
|
5732
|
-
touch-action: none;
|
|
5733
|
-
}
|
|
5734
|
-
.oc-data-table-resize::after {
|
|
5735
|
-
content: "";
|
|
5736
|
-
position: absolute;
|
|
5737
|
-
inset-block: 0.4rem;
|
|
5738
|
-
inset-inline-start: 0.25rem;
|
|
5739
|
-
inline-size: 1px;
|
|
5740
|
-
background: var(--oc-border);
|
|
5741
|
-
}
|
|
5742
|
-
.oc-data-table.is-resizing { cursor: col-resize; user-select: none; }
|
|
5743
|
-
/* After a resize the table grows with its columns and the frame scrolls sideways. */
|
|
5744
|
-
.oc-data-table-table--sized { inline-size: max-content; min-inline-size: 100%; }
|
|
6075
|
+
/* Header label (column width by dragging is OmniaUI Pro: app-data-table-pro.css). */
|
|
6076
|
+
.oc-data-table-th { display: flex; align-items: center; gap: 0.25rem; min-inline-size: 0; }
|
|
5745
6077
|
|
|
5746
6078
|
/* Narrow control columns. The actions column stays on the right while the rest scrolls. */
|
|
5747
6079
|
.oc-data-table .oc-table :is(th, td)[data-oc-col="select"],
|
|
@@ -5757,8 +6089,8 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
5757
6089
|
background: var(--oc-card);
|
|
5758
6090
|
box-shadow: -1px 0 0 var(--oc-border);
|
|
5759
6091
|
}
|
|
5760
|
-
.oc-data-table .oc-table thead th[data-oc-col="actions"] { z-index: 3; }
|
|
5761
|
-
.oc-data-table .oc-table tbody tr:nth-child(even) > td[data-oc-col="actions"] { background:
|
|
6092
|
+
.oc-data-table .oc-table thead th[data-oc-col="actions"] { z-index: 3; background: var(--oc-card); }
|
|
6093
|
+
.oc-data-table .oc-table tbody tr:is(:nth-child(even of :not([hidden])), :hover) > td[data-oc-col="actions"] { background: inherit; }
|
|
5762
6094
|
|
|
5763
6095
|
/* Trash and archive rows stay readable but quieter than active rows. */
|
|
5764
6096
|
.oc-data-table tr[data-oc-status="deleted"],
|
|
@@ -5787,55 +6119,24 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
5787
6119
|
padding: 0.75rem;
|
|
5788
6120
|
}
|
|
5789
6121
|
.oc-data-table-card {
|
|
5790
|
-
|
|
6122
|
+
display: flex;
|
|
6123
|
+
flex-direction: column;
|
|
6124
|
+
gap: 0.75rem;
|
|
6125
|
+
padding: 0.875rem;
|
|
5791
6126
|
background: var(--oc-card);
|
|
5792
6127
|
border: 1px solid var(--oc-border);
|
|
5793
6128
|
border-radius: calc(var(--oc-radius) - 2px);
|
|
5794
6129
|
}
|
|
5795
6130
|
.oc-data-table-card-title { margin: 0; font-size: 0.875rem; font-weight: 600; }
|
|
5796
|
-
|
|
6131
|
+
.oc-data-table-card-title .oc-data-table-name-sub { font-weight: 400; }
|
|
5797
6132
|
.oc-data-table-empty { margin: 0; padding: 2rem 1rem; text-align: center; color: var(--oc-muted-foreground); }
|
|
5798
6133
|
.oc-data-table-empty[hidden] { display: none; }
|
|
5799
6134
|
|
|
5800
|
-
/* Footer: count on the left, rows-per-page in the middle, pager on the right. */
|
|
5801
|
-
.oc-data-table-footer {
|
|
5802
|
-
display: grid;
|
|
5803
|
-
grid-template-columns: 1fr auto 1fr;
|
|
5804
|
-
align-items: center;
|
|
5805
|
-
gap: 0.75rem;
|
|
5806
|
-
}
|
|
5807
|
-
.oc-data-table-summary { margin: 0; font-size: 0.8125rem; color: var(--oc-muted-foreground); }
|
|
5808
|
-
.oc-data-table-per-page { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; }
|
|
5809
|
-
.oc-data-table-per-page .oc-select { inline-size: auto; }
|
|
5810
|
-
.oc-data-table-pager { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.25rem; }
|
|
5811
|
-
.oc-data-table-page {
|
|
5812
|
-
min-inline-size: 2rem;
|
|
5813
|
-
padding: 0.25rem 0.45rem;
|
|
5814
|
-
font: inherit;
|
|
5815
|
-
font-size: 0.8125rem;
|
|
5816
|
-
color: var(--oc-foreground);
|
|
5817
|
-
background: var(--oc-card);
|
|
5818
|
-
border: 1px solid var(--oc-border);
|
|
5819
|
-
border-radius: calc(var(--oc-radius) - 2px);
|
|
5820
|
-
cursor: pointer;
|
|
5821
|
-
}
|
|
5822
|
-
.oc-data-table-page[aria-current="page"] {
|
|
5823
|
-
color: var(--oc-primary);
|
|
5824
|
-
background: oklch(from var(--oc-primary) l c h / 0.1);
|
|
5825
|
-
border-color: oklch(from var(--oc-primary) l c h / 0.35);
|
|
5826
|
-
}
|
|
5827
|
-
.oc-data-table-page:disabled { opacity: 0.4; cursor: default; }
|
|
5828
|
-
.oc-data-table-page:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
5829
|
-
.oc-data-table-ellipsis { padding-inline: 0.25rem; color: var(--oc-muted-foreground); }
|
|
5830
6135
|
|
|
5831
6136
|
/* Design choice from the gear menu: tighter rows, like the compact article lists. */
|
|
5832
6137
|
.oc-data-table--compact .oc-table :is(th, td) { padding: 0.25rem 0.5rem; font-size: 0.8125rem; }
|
|
5833
6138
|
.oc-data-table--compact .oc-table thead th { block-size: 2rem; }
|
|
5834
6139
|
|
|
5835
|
-
@media (max-width: 40rem) {
|
|
5836
|
-
.oc-data-table-footer { grid-template-columns: 1fr; }
|
|
5837
|
-
.oc-data-table-pager { justify-content: flex-start; }
|
|
5838
|
-
}
|
|
5839
6140
|
@media (prefers-reduced-motion: reduce) {
|
|
5840
6141
|
.oc-data-table .oc-table tbody tr { transition: none; }
|
|
5841
6142
|
}
|
|
@@ -5972,6 +6273,73 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
5972
6273
|
.oc-list-toolbar-view { transition: none; }
|
|
5973
6274
|
}
|
|
5974
6275
|
|
|
6276
|
+
/* parts/app-name-card.css */
|
|
6277
|
+
/* File: packages/ui/src/parts/app-name-card.css | Map: card (details) -> head (chevron, name, rename, amount, actions) -> rename input -> body -> nested cards */
|
|
6278
|
+
/* Name card: a collapsible card for one part of a quote or plan,
|
|
6279
|
+
with its name, a rename pencil, an optional amount and a delete button. Native <details>: it opens without script;
|
|
6280
|
+
packages/ui/src/name-card.js adds the rename (pencil -> input, Enter saves, Escape cancels). */
|
|
6281
|
+
|
|
6282
|
+
.oc-name-card {
|
|
6283
|
+
background: var(--oc-card);
|
|
6284
|
+
border: 1px solid var(--oc-border);
|
|
6285
|
+
border-radius: var(--oc-radius);
|
|
6286
|
+
}
|
|
6287
|
+
.oc-name-card + .oc-name-card { margin-block-start: 0.5rem; }
|
|
6288
|
+
.oc-name-card__head {
|
|
6289
|
+
display: flex;
|
|
6290
|
+
align-items: center;
|
|
6291
|
+
gap: 0.5rem;
|
|
6292
|
+
min-block-size: 2.75rem;
|
|
6293
|
+
padding: 0.375rem 0.5rem 0.375rem 0.75rem;
|
|
6294
|
+
list-style: none;
|
|
6295
|
+
cursor: pointer;
|
|
6296
|
+
}
|
|
6297
|
+
.oc-name-card__head::-webkit-details-marker { display: none; }
|
|
6298
|
+
.oc-name-card__head::before {
|
|
6299
|
+
content: "";
|
|
6300
|
+
flex-shrink: 0;
|
|
6301
|
+
inline-size: 1rem;
|
|
6302
|
+
block-size: 1rem;
|
|
6303
|
+
background: var(--oc-muted-foreground);
|
|
6304
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8.59 16.58L13.17 12L8.59 7.41L10 6l6 6l-6 6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
6305
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8.59 16.58L13.17 12L8.59 7.41L10 6l6 6l-6 6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
6306
|
+
transition: rotate var(--oc-transition-fast);
|
|
6307
|
+
}
|
|
6308
|
+
.oc-name-card[open] > .oc-name-card__head::before { rotate: 90deg; }
|
|
6309
|
+
.oc-name-card__head:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; border-radius: var(--oc-radius); }
|
|
6310
|
+
.oc-name-card__title { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
|
|
6311
|
+
.oc-name-card__meta { font-size: 0.75rem; color: var(--oc-muted-foreground); white-space: nowrap; }
|
|
6312
|
+
.oc-name-card__head .oc-btn { flex-shrink: 0; }
|
|
6313
|
+
/* Amount and actions sit on the right; the amount is tabular so a column of cards lines up. */
|
|
6314
|
+
.oc-name-card__amount { margin-inline-start: auto; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
6315
|
+
.oc-name-card__actions { display: inline-flex; flex-shrink: 0; gap: 0.25rem; margin-inline-start: auto; }
|
|
6316
|
+
.oc-name-card__amount + .oc-name-card__actions { margin-inline-start: 0.5rem; }
|
|
6317
|
+
|
|
6318
|
+
/* Rename: the title becomes an input of the same size (hidden toggles between the two). */
|
|
6319
|
+
.oc-name-card__input {
|
|
6320
|
+
flex: 1;
|
|
6321
|
+
min-inline-size: 0;
|
|
6322
|
+
block-size: 2rem;
|
|
6323
|
+
padding: 0 0.5rem;
|
|
6324
|
+
font: inherit;
|
|
6325
|
+
font-weight: 600;
|
|
6326
|
+
color: var(--oc-foreground);
|
|
6327
|
+
background: var(--oc-background);
|
|
6328
|
+
border: 1px solid var(--oc-ring);
|
|
6329
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
6330
|
+
}
|
|
6331
|
+
.oc-name-card__input[hidden],
|
|
6332
|
+
.oc-name-card__title[hidden] { display: none; }
|
|
6333
|
+
|
|
6334
|
+
.oc-name-card__body { padding: 0.75rem 0.75rem 0.875rem 2.25rem; border-block-start: 1px solid var(--oc-border); }
|
|
6335
|
+
/* Nested cards (the parts of a title) use a quieter surface; --total colors the amount of the main card. */
|
|
6336
|
+
.oc-name-card__body > .oc-name-card { background: var(--oc-surface); }
|
|
6337
|
+
.oc-name-card--total > .oc-name-card__head .oc-name-card__amount { color: var(--oc-primary); }
|
|
6338
|
+
|
|
6339
|
+
@media (prefers-reduced-motion: reduce) {
|
|
6340
|
+
.oc-name-card__head::before { transition: none; }
|
|
6341
|
+
}
|
|
6342
|
+
|
|
5975
6343
|
/* parts/app-page-header.css */
|
|
5976
6344
|
/* File: packages/ui/src/parts/app-page-header.css | Map: header (container) -> back link row -> main row (text + actions) -> title + subtitle + meta -> wide layout (@container) -> modifiers (bordered, sticky, sm) */
|
|
5977
6345
|
/* Page header: back link + title + subtitle + actions at the top of an app page.
|
|
@@ -6028,6 +6396,47 @@ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
|
|
|
6028
6396
|
.oc-page-header--sm { padding-block: 0.5rem 0.75rem; }
|
|
6029
6397
|
.oc-page-header--sm .oc-page-header-title { font-size: 1.125rem; }
|
|
6030
6398
|
|
|
6399
|
+
/* parts/app-result.css */
|
|
6400
|
+
/* File: packages/ui/src/parts/app-result.css */
|
|
6401
|
+
/* Map: result -> icon tile -> title -> text -> actions */
|
|
6402
|
+
/* The outcome of an action in the place where the form was: "Thank you, your message was sent" with an icon tile, a
|
|
6403
|
+
short text and a button ("Send another message"). Give it role="status" and tabindex="-1" and move the focus to it.
|
|
6404
|
+
--center centers it; the tile takes its color from an .oc-accent--* preset. */
|
|
6405
|
+
|
|
6406
|
+
.oc-result {
|
|
6407
|
+
display: grid;
|
|
6408
|
+
justify-items: start;
|
|
6409
|
+
gap: 0.75rem;
|
|
6410
|
+
}
|
|
6411
|
+
.oc-result:focus { outline: none; }
|
|
6412
|
+
.oc-result[hidden] { display: none; }
|
|
6413
|
+
.oc-result > .oc-icon-tile { margin-block-end: 0.35rem; }
|
|
6414
|
+
.oc-result__title {
|
|
6415
|
+
margin: 0;
|
|
6416
|
+
font-size: 1.2rem;
|
|
6417
|
+
line-height: 1.25;
|
|
6418
|
+
}
|
|
6419
|
+
.oc-result__text {
|
|
6420
|
+
margin: 0;
|
|
6421
|
+
color: var(--oc-muted-foreground);
|
|
6422
|
+
}
|
|
6423
|
+
.oc-result__text a {
|
|
6424
|
+
color: var(--oc-primary);
|
|
6425
|
+
text-decoration: underline;
|
|
6426
|
+
text-underline-offset: 3px;
|
|
6427
|
+
}
|
|
6428
|
+
.oc-result__actions {
|
|
6429
|
+
display: flex;
|
|
6430
|
+
flex-wrap: wrap;
|
|
6431
|
+
gap: 0.75rem;
|
|
6432
|
+
margin-block-start: 0.5rem;
|
|
6433
|
+
}
|
|
6434
|
+
.oc-result--center {
|
|
6435
|
+
justify-items: center;
|
|
6436
|
+
text-align: center;
|
|
6437
|
+
}
|
|
6438
|
+
.oc-result--center .oc-result__actions { justify-content: center; }
|
|
6439
|
+
|
|
6031
6440
|
/* parts/app-severity-card.css */
|
|
6032
6441
|
/* File: packages/ui/src/parts/app-severity-card.css | Map: card (details) -> head (summary) -> icon + title + tags -> level badge + chevron -> body + actions -> severity modifiers -> resolved state -> reduced motion */
|
|
6033
6442
|
/* Severity card: risk-colored expandable card (security finding, incident, audit item).
|
|
@@ -6456,6 +6865,11 @@ button.oc-stat-card:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
6456
6865
|
min-block-size: 0;
|
|
6457
6866
|
timeline-scope: --oc-titlebar-scroll;
|
|
6458
6867
|
}
|
|
6868
|
+
/* A card with a title bar on top: the bar runs edge to edge, its last button follows the card corner. */
|
|
6869
|
+
.oc-card:has(> .oc-titlebar:first-child) { padding: 0; --oc-titlebar-radius: calc(var(--oc-radius) + 3px); }
|
|
6870
|
+
/* As the heading of a card section (bar + .oc-card-content): the title lines up with the content below. */
|
|
6871
|
+
.oc-card > .oc-titlebar:first-child:has(+ .oc-card-content) { padding-inline-start: 24px; }
|
|
6872
|
+
.oc-card > .oc-titlebar:first-child:has(+ .oc-card-content) .oc-titlebar__title { font-size: 0.9375rem; }
|
|
6459
6873
|
|
|
6460
6874
|
.oc-titlebar {
|
|
6461
6875
|
display: flex;
|
|
@@ -6463,7 +6877,7 @@ button.oc-stat-card:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
6463
6877
|
align-items: center;
|
|
6464
6878
|
gap: 2px;
|
|
6465
6879
|
min-block-size: 46px;
|
|
6466
|
-
padding: 0
|
|
6880
|
+
padding: 0 0 0 14px;
|
|
6467
6881
|
border-block-end: 1px solid transparent;
|
|
6468
6882
|
-webkit-user-select: none;
|
|
6469
6883
|
user-select: none;
|
|
@@ -6480,7 +6894,7 @@ button.oc-stat-card:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
6480
6894
|
/* Title area: grows, cuts the subtitle off with …. */
|
|
6481
6895
|
.oc-titlebar__main { display: flex; flex: 1; align-items: center; gap: 8px; min-inline-size: 0; }
|
|
6482
6896
|
.oc-titlebar__icon { flex: none; inline-size: 18px; block-size: 18px; }
|
|
6483
|
-
.oc-titlebar__title { flex: none; font-size: 0.8667rem; font-weight: 650; letter-spacing: -0.01em; }
|
|
6897
|
+
.oc-titlebar__title { flex: none; margin: 0; font-size: 0.8667rem; font-weight: 650; letter-spacing: -0.01em; }
|
|
6484
6898
|
.oc-titlebar__subtitle {
|
|
6485
6899
|
min-inline-size: 0;
|
|
6486
6900
|
overflow: hidden;
|
|
@@ -6490,17 +6904,19 @@ button.oc-stat-card:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
6490
6904
|
white-space: nowrap;
|
|
6491
6905
|
}
|
|
6492
6906
|
.oc-titlebar__subtitle::before { content: "·"; margin-inline-end: 8px; }
|
|
6493
|
-
|
|
6907
|
+
/* Window buttons sit in the top right corner (as on a desktop window), not centred on the title line. */
|
|
6908
|
+
.oc-titlebar__actions { display: flex; flex: none; align-self: flex-start; align-items: flex-start; margin-inline-start: auto; }
|
|
6494
6909
|
|
|
6495
|
-
/*
|
|
6910
|
+
/* Window buttons: muted, a light surface on hover, the close button red. The last one follows the
|
|
6911
|
+
rounded corner of the card or window around the bar (--oc-titlebar-radius). */
|
|
6496
6912
|
.oc-titlebar__btn {
|
|
6497
6913
|
display: inline-grid;
|
|
6498
6914
|
place-items: center;
|
|
6499
|
-
inline-size:
|
|
6500
|
-
block-size:
|
|
6915
|
+
inline-size: 40px;
|
|
6916
|
+
block-size: 34px;
|
|
6501
6917
|
padding: 0;
|
|
6502
6918
|
border: 0;
|
|
6503
|
-
border-radius:
|
|
6919
|
+
border-radius: 0;
|
|
6504
6920
|
background: transparent;
|
|
6505
6921
|
color: var(--oc-muted-foreground);
|
|
6506
6922
|
font: inherit;
|
|
@@ -6511,6 +6927,7 @@ button.oc-stat-card:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
6511
6927
|
.oc-titlebar__btn:hover { background: var(--oc-glass-panel-strong, var(--oc-accent)); color: var(--oc-foreground); }
|
|
6512
6928
|
.oc-titlebar__btn:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
|
|
6513
6929
|
.oc-titlebar__btn--close:hover { background: var(--oc-destructive); color: var(--oc-destructive-foreground); }
|
|
6930
|
+
.oc-titlebar__actions > .oc-titlebar__btn:last-child { border-start-end-radius: var(--oc-titlebar-radius, calc(var(--oc-radius) - 1px)); }
|
|
6514
6931
|
|
|
6515
6932
|
/* Body: the part that scrolls under the bar. */
|
|
6516
6933
|
.oc-titlebar-body {
|
|
@@ -6535,7 +6952,7 @@ button.oc-stat-card:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
6535
6952
|
|
|
6536
6953
|
/* Sizes */
|
|
6537
6954
|
.oc-titlebar--sm { min-block-size: 36px; padding-inline-start: 10px; }
|
|
6538
|
-
.oc-titlebar--sm .oc-titlebar__btn { inline-size:
|
|
6955
|
+
.oc-titlebar--sm .oc-titlebar__btn { inline-size: 34px; block-size: 28px; }
|
|
6539
6956
|
|
|
6540
6957
|
@media (prefers-reduced-motion: reduce) {
|
|
6541
6958
|
.oc-titlebar-window > .oc-titlebar { animation: none; }
|
|
@@ -7060,6 +7477,229 @@ a.oc-tree-label:focus-visible { outline: 2px solid var(--oc-ring); outline-offse
|
|
|
7060
7477
|
.oc-chart-line__dot, .oc-chart-line__tip { transition: none; }
|
|
7061
7478
|
}
|
|
7062
7479
|
|
|
7480
|
+
/* parts/data-meter-row.css */
|
|
7481
|
+
/* File: packages/ui/src/parts/data-meter-row.css | Map: row grid -> label and value -> bar and fill -> levels -> reset line -> list */
|
|
7482
|
+
/* Usage meter row: a label, the usage as text, a bar and when it resets. The fill comes from
|
|
7483
|
+
--oc-meter (0–100%); data-level="warn" or "critical" changes the color. */
|
|
7484
|
+
|
|
7485
|
+
.oc-meter-row {
|
|
7486
|
+
display: grid;
|
|
7487
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
7488
|
+
align-items: baseline;
|
|
7489
|
+
gap: 0.375rem 0.75rem;
|
|
7490
|
+
}
|
|
7491
|
+
.oc-meter-row__label { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.875rem; font-weight: 600; }
|
|
7492
|
+
.oc-meter-row__value { font-size: 0.8125rem; font-variant-numeric: tabular-nums; color: var(--oc-muted-foreground); white-space: nowrap; }
|
|
7493
|
+
.oc-meter-row__value strong { color: var(--oc-foreground); }
|
|
7494
|
+
.oc-meter-row__bar {
|
|
7495
|
+
grid-column: 1 / -1;
|
|
7496
|
+
block-size: 0.5rem;
|
|
7497
|
+
overflow: hidden;
|
|
7498
|
+
background: var(--oc-muted);
|
|
7499
|
+
border-radius: 999px;
|
|
7500
|
+
}
|
|
7501
|
+
.oc-meter-row__bar::before {
|
|
7502
|
+
content: "";
|
|
7503
|
+
display: block;
|
|
7504
|
+
inline-size: clamp(0%, var(--oc-meter, 0%), 100%);
|
|
7505
|
+
block-size: 100%;
|
|
7506
|
+
background: var(--oc-meter-color, var(--oc-success));
|
|
7507
|
+
border-radius: inherit;
|
|
7508
|
+
transition: inline-size 0.4s ease;
|
|
7509
|
+
}
|
|
7510
|
+
.oc-meter-row[data-level="warn"] { --oc-meter-color: var(--oc-warning); }
|
|
7511
|
+
.oc-meter-row[data-level="critical"] { --oc-meter-color: var(--oc-destructive); }
|
|
7512
|
+
.oc-meter-row[data-level="critical"] .oc-meter-row__value strong { color: var(--oc-destructive); }
|
|
7513
|
+
.oc-meter-row__reset { grid-column: 1 / -1; display: inline-flex; align-items: center; gap: 0.25rem; font-size: 0.75rem; color: var(--oc-muted-foreground); }
|
|
7514
|
+
.oc-meter-list { display: grid; gap: 1rem; }
|
|
7515
|
+
.oc-meter-list--compact { gap: 0.625rem; }
|
|
7516
|
+
.oc-meter-list--compact .oc-meter-row__bar { block-size: 0.375rem; }
|
|
7517
|
+
|
|
7518
|
+
@media (prefers-reduced-motion: reduce) {
|
|
7519
|
+
.oc-meter-row__bar::before { transition: none; }
|
|
7520
|
+
}
|
|
7521
|
+
|
|
7522
|
+
/* parts/data-price.css */
|
|
7523
|
+
/* File: packages/ui/src/parts/data-price.css | Map: price block -> main line (amount, old price, discount) -> VAT note and alternative -> sizes and sale */
|
|
7524
|
+
/* A price as shops and quotes show it: the amount, an optional old price and discount badge, a VAT note
|
|
7525
|
+
("excl. VAT") and the other amount ("€ 121,00 incl. VAT"). Numbers use tabular digits. */
|
|
7526
|
+
|
|
7527
|
+
.oc-price { display: inline-grid; gap: 0.125rem; min-inline-size: 0; }
|
|
7528
|
+
.oc-price__main { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; }
|
|
7529
|
+
.oc-price__amount { font-size: 1.25rem; font-weight: 700; line-height: 1.2; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
7530
|
+
.oc-price__old { font-size: 0.875rem; color: var(--oc-muted-foreground); text-decoration: line-through; text-decoration-thickness: 1px; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
7531
|
+
.oc-price__main > .oc-badge { align-self: center; }
|
|
7532
|
+
.oc-price__note { font-size: 0.75rem; font-weight: 500; color: var(--oc-muted-foreground); }
|
|
7533
|
+
.oc-price__alt { font-size: 0.8125rem; color: var(--oc-muted-foreground); font-variant-numeric: tabular-nums; }
|
|
7534
|
+
|
|
7535
|
+
.oc-price--sm .oc-price__amount { font-size: 1rem; }
|
|
7536
|
+
.oc-price--lg .oc-price__amount { font-size: 1.75rem; }
|
|
7537
|
+
.oc-price--sale .oc-price__amount { color: var(--oc-destructive); }
|
|
7538
|
+
/* Inline: amount and note on one line (table cells, lists). */
|
|
7539
|
+
.oc-price--inline { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; }
|
|
7540
|
+
.oc-price--inline .oc-price__amount { font-size: inherit; font-weight: 600; }
|
|
7541
|
+
td[data-numeric] > .oc-price, .oc-price--end { justify-items: end; text-align: end; }
|
|
7542
|
+
td[data-numeric] > .oc-price .oc-price__main, .oc-price--end .oc-price__main { justify-content: flex-end; }
|
|
7543
|
+
|
|
7544
|
+
/* parts/data-ribbon.css */
|
|
7545
|
+
/* File: packages/ui/src/parts/data-ribbon.css | Map: host (positioned, clipped) -> ribbon band -> four corners -> tones -> size -> RTL */
|
|
7546
|
+
/* Corner ribbon: a diagonal band across one corner of a card, an image or a document ("Paid", "New", "Sale").
|
|
7547
|
+
Put .oc-ribbon as a child of the box; the box gets position: relative and clips the band (zero specificity,
|
|
7548
|
+
so your own position or overflow wins). The text is real text, so screen readers read it. */
|
|
7549
|
+
|
|
7550
|
+
:where(:has(> .oc-ribbon)) { position: relative; overflow: clip; }
|
|
7551
|
+
|
|
7552
|
+
.oc-ribbon {
|
|
7553
|
+
/* Distance from the corner to the middle of the band, along both edges. */
|
|
7554
|
+
--oc-ribbon-inset: 2.25rem;
|
|
7555
|
+
--oc-ribbon-x: 1;
|
|
7556
|
+
--oc-ribbon-y: -1;
|
|
7557
|
+
--oc-ribbon-turn: 45deg;
|
|
7558
|
+
--oc-ribbon-bg: var(--oc-primary);
|
|
7559
|
+
--oc-ribbon-fg: var(--oc-primary-foreground);
|
|
7560
|
+
position: absolute;
|
|
7561
|
+
z-index: 2;
|
|
7562
|
+
inset-block-start: var(--oc-ribbon-inset);
|
|
7563
|
+
inset-inline-end: var(--oc-ribbon-inset);
|
|
7564
|
+
inline-size: calc(var(--oc-ribbon-inset) * 2.83 + 3rem);
|
|
7565
|
+
padding: 0.3em 0;
|
|
7566
|
+
overflow: hidden;
|
|
7567
|
+
color: var(--oc-ribbon-fg);
|
|
7568
|
+
background: var(--oc-ribbon-bg);
|
|
7569
|
+
box-shadow: 0 1px 3px oklch(0 0 0 / 0.18);
|
|
7570
|
+
font-size: 0.75rem;
|
|
7571
|
+
font-weight: 700;
|
|
7572
|
+
line-height: 1.25;
|
|
7573
|
+
letter-spacing: 0.08em;
|
|
7574
|
+
text-align: center;
|
|
7575
|
+
text-overflow: ellipsis;
|
|
7576
|
+
text-transform: uppercase;
|
|
7577
|
+
white-space: nowrap;
|
|
7578
|
+
pointer-events: none;
|
|
7579
|
+
translate: calc(50% * var(--oc-ribbon-x)) calc(50% * var(--oc-ribbon-y));
|
|
7580
|
+
rotate: var(--oc-ribbon-turn);
|
|
7581
|
+
}
|
|
7582
|
+
|
|
7583
|
+
/* Corners: top end is the default. */
|
|
7584
|
+
.oc-ribbon--top-start {
|
|
7585
|
+
inset-inline-end: auto;
|
|
7586
|
+
inset-inline-start: var(--oc-ribbon-inset);
|
|
7587
|
+
--oc-ribbon-x: -1;
|
|
7588
|
+
--oc-ribbon-turn: -45deg;
|
|
7589
|
+
}
|
|
7590
|
+
.oc-ribbon--bottom-end {
|
|
7591
|
+
inset-block-start: auto;
|
|
7592
|
+
inset-block-end: var(--oc-ribbon-inset);
|
|
7593
|
+
--oc-ribbon-y: 1;
|
|
7594
|
+
--oc-ribbon-turn: -45deg;
|
|
7595
|
+
}
|
|
7596
|
+
.oc-ribbon--bottom-start {
|
|
7597
|
+
inset-block-start: auto;
|
|
7598
|
+
inset-block-end: var(--oc-ribbon-inset);
|
|
7599
|
+
inset-inline-end: auto;
|
|
7600
|
+
inset-inline-start: var(--oc-ribbon-inset);
|
|
7601
|
+
--oc-ribbon-x: -1;
|
|
7602
|
+
--oc-ribbon-y: 1;
|
|
7603
|
+
}
|
|
7604
|
+
|
|
7605
|
+
/* Tones. */
|
|
7606
|
+
.oc-ribbon--success { --oc-ribbon-bg: var(--oc-success); --oc-ribbon-fg: var(--oc-success-foreground); }
|
|
7607
|
+
.oc-ribbon--warning { --oc-ribbon-bg: var(--oc-warning); --oc-ribbon-fg: var(--oc-warning-foreground); }
|
|
7608
|
+
.oc-ribbon--destructive { --oc-ribbon-bg: var(--oc-destructive); --oc-ribbon-fg: var(--oc-destructive-foreground); }
|
|
7609
|
+
.oc-ribbon--muted { --oc-ribbon-bg: var(--oc-muted); --oc-ribbon-fg: var(--oc-muted-foreground); }
|
|
7610
|
+
|
|
7611
|
+
/* Sizes: a smaller band for a product card, a larger one for a document or a hero image. */
|
|
7612
|
+
.oc-ribbon--sm { --oc-ribbon-inset: 1.6rem; font-size: 0.625rem; }
|
|
7613
|
+
.oc-ribbon--lg { --oc-ribbon-inset: 2.9rem; font-size: 0.875rem; }
|
|
7614
|
+
|
|
7615
|
+
/* Right to left: start and end swap sides, so the band leans the other way. */
|
|
7616
|
+
.oc-ribbon:dir(rtl) { translate: calc(-50% * var(--oc-ribbon-x)) calc(50% * var(--oc-ribbon-y)); rotate: calc(var(--oc-ribbon-turn) * -1); }
|
|
7617
|
+
|
|
7618
|
+
@media (forced-colors: active) {
|
|
7619
|
+
.oc-ribbon { forced-color-adjust: none; color: Canvas; background: CanvasText; }
|
|
7620
|
+
}
|
|
7621
|
+
|
|
7622
|
+
/* parts/data-spec-list.css */
|
|
7623
|
+
/* File: packages/ui/src/parts/data-spec-list.css | Map: list (container) -> groups in columns -> group title -> rows (term / value) -> stack modifier */
|
|
7624
|
+
/* Technical specifications in named groups (Dimensions, Electrical, Package …): label / value rows with tabular
|
|
7625
|
+
digits. Two columns of groups once the list is wide enough (container query), one column with --stack. */
|
|
7626
|
+
|
|
7627
|
+
.oc-spec-list { container-type: inline-size; inline-size: 100%; display: grid; gap: 1.25rem; min-inline-size: 0; }
|
|
7628
|
+
@container (min-width: 40rem) {
|
|
7629
|
+
.oc-spec-list:not(.oc-spec-list--stack) { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: 1.25rem 2rem; }
|
|
7630
|
+
}
|
|
7631
|
+
.oc-spec-group { min-inline-size: 0; }
|
|
7632
|
+
.oc-spec-group__title {
|
|
7633
|
+
display: flex;
|
|
7634
|
+
align-items: center;
|
|
7635
|
+
gap: 0.5rem;
|
|
7636
|
+
margin: 0 0 0.25rem;
|
|
7637
|
+
padding-block-end: 0.375rem;
|
|
7638
|
+
font-size: 0.75rem;
|
|
7639
|
+
font-weight: 600;
|
|
7640
|
+
letter-spacing: 0.04em;
|
|
7641
|
+
text-transform: uppercase;
|
|
7642
|
+
color: var(--oc-muted-foreground);
|
|
7643
|
+
border-block-end: 1px solid var(--oc-border);
|
|
7644
|
+
}
|
|
7645
|
+
.oc-spec-group__title > :is(svg, iconify-icon) { inline-size: 1rem; block-size: 1rem; font-size: 1rem; }
|
|
7646
|
+
.oc-spec-group__rows { margin: 0; }
|
|
7647
|
+
.oc-spec-row {
|
|
7648
|
+
display: grid;
|
|
7649
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
|
|
7650
|
+
gap: 1rem;
|
|
7651
|
+
padding: 0.4rem 0;
|
|
7652
|
+
font-size: 0.875rem;
|
|
7653
|
+
border-block-end: 1px dashed var(--oc-border);
|
|
7654
|
+
}
|
|
7655
|
+
.oc-spec-row:last-child { border-block-end: 0; }
|
|
7656
|
+
.oc-spec-row > dt { margin: 0; color: var(--oc-muted-foreground); }
|
|
7657
|
+
.oc-spec-row > dd { margin: 0; min-inline-size: 0; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
|
|
7658
|
+
.oc-spec-row > dd > small { margin-inline-start: 0.25rem; font-weight: 400; color: var(--oc-muted-foreground); }
|
|
7659
|
+
|
|
7660
|
+
/* parts/data-stock.css */
|
|
7661
|
+
/* File: packages/ui/src/parts/data-stock.css | Map: indicator (dot + text) -> levels (ok, low, out, order) -> badge variant -> count */
|
|
7662
|
+
/* Stock status: a colored dot with a short text ("In stock", "Only 3 left", "Out of stock", "On order").
|
|
7663
|
+
data-level picks the color; the text always says it too, so color is never the only signal. */
|
|
7664
|
+
|
|
7665
|
+
.oc-stock {
|
|
7666
|
+
--oc-stock-color: var(--oc-success);
|
|
7667
|
+
/* Inline text (not flex), so "Only <b>3</b> left" keeps normal word spacing. */
|
|
7668
|
+
display: inline-block;
|
|
7669
|
+
font-size: 0.8125rem;
|
|
7670
|
+
font-weight: 500;
|
|
7671
|
+
line-height: 1.3;
|
|
7672
|
+
color: var(--oc-foreground);
|
|
7673
|
+
white-space: nowrap;
|
|
7674
|
+
}
|
|
7675
|
+
.oc-stock::before {
|
|
7676
|
+
content: "";
|
|
7677
|
+
display: inline-block;
|
|
7678
|
+
margin-inline-end: 0.4rem;
|
|
7679
|
+
vertical-align: 0.05em;
|
|
7680
|
+
inline-size: 0.5rem;
|
|
7681
|
+
block-size: 0.5rem;
|
|
7682
|
+
background: var(--oc-stock-color);
|
|
7683
|
+
border-radius: 50%;
|
|
7684
|
+
box-shadow: 0 0 0 3px oklch(from var(--oc-stock-color) l c h / 0.18);
|
|
7685
|
+
}
|
|
7686
|
+
.oc-stock[data-level="low"] { --oc-stock-color: var(--oc-warning); }
|
|
7687
|
+
.oc-stock[data-level="out"] { --oc-stock-color: var(--oc-destructive); }
|
|
7688
|
+
.oc-stock[data-level="order"] { --oc-stock-color: var(--oc-info, light-dark(var(--color-deepskyblue-70), var(--color-skyblue-50))); }
|
|
7689
|
+
.oc-stock__count { font-weight: 700; font-variant-numeric: tabular-nums; }
|
|
7690
|
+
.oc-stock__note { font-weight: 400; color: var(--oc-muted-foreground); }
|
|
7691
|
+
|
|
7692
|
+
/* Badge: a tinted pill (table cells, product cards). */
|
|
7693
|
+
.oc-stock--badge {
|
|
7694
|
+
padding: 0.125rem 0.5rem 0.125rem 0.45rem;
|
|
7695
|
+
font-size: 0.75rem;
|
|
7696
|
+
color: color-mix(in oklch, var(--oc-stock-color) 75%, var(--oc-foreground));
|
|
7697
|
+
background: oklch(from var(--oc-stock-color) l c h / 0.12);
|
|
7698
|
+
border: 1px solid oklch(from var(--oc-stock-color) l c h / 0.3);
|
|
7699
|
+
border-radius: 999px;
|
|
7700
|
+
}
|
|
7701
|
+
.oc-stock--badge::before { inline-size: 0.4rem; block-size: 0.4rem; margin-inline-end: 0.3rem; vertical-align: 0.1em; box-shadow: none; }
|
|
7702
|
+
|
|
7063
7703
|
/* parts/forms-affix-input.css */
|
|
7064
7704
|
/* File: packages/ui/src/parts/forms-affix-input.css
|
|
7065
7705
|
Map: --affix group (frame on the wrapper) -> inner input -> inline affix (€, %, @) -> states -> --end alignment
|
|
@@ -7088,7 +7728,7 @@ a.oc-tree-label:focus-visible { outline: 2px solid var(--oc-ring); outline-offse
|
|
|
7088
7728
|
min-inline-size: 0;
|
|
7089
7729
|
}
|
|
7090
7730
|
|
|
7091
|
-
/* Symbol column: same chrome as the
|
|
7731
|
+
/* Symbol column: the same chrome as the other fields (€, %, @, barcode icon).
|
|
7092
7732
|
A divider separates the symbol from the value. */
|
|
7093
7733
|
.oc-input-group.oc-input-group--affix .oc-input-addon {
|
|
7094
7734
|
align-self: stretch;
|
|
@@ -7137,6 +7777,51 @@ a.oc-tree-label:focus-visible { outline: 2px solid var(--oc-ring); outline-offse
|
|
|
7137
7777
|
}
|
|
7138
7778
|
.oc-input-group--affix:has(.oc-input:disabled) .oc-input { opacity: 1; }
|
|
7139
7779
|
|
|
7780
|
+
/* parts/forms-avatar-picker.css */
|
|
7781
|
+
/* File: packages/ui/src/parts/forms-avatar-picker.css | Map: picker -> preview (initials or image) -> file input -> text and buttons -> drop state -> square and logo */
|
|
7782
|
+
/* Logo or avatar picker: a preview, "Upload" and "Remove", a hint, and dropping a file
|
|
7783
|
+
on the preview. The file input is a real <input type="file">; packages/ui/src/avatar-picker.js shows the preview. */
|
|
7784
|
+
|
|
7785
|
+
.oc-avatar-picker { display: flex; align-items: center; gap: 1rem; }
|
|
7786
|
+
.oc-avatar-picker__preview {
|
|
7787
|
+
position: relative;
|
|
7788
|
+
display: inline-flex;
|
|
7789
|
+
align-items: center;
|
|
7790
|
+
justify-content: center;
|
|
7791
|
+
flex-shrink: 0;
|
|
7792
|
+
inline-size: var(--oc-avatar-picker-size, 4.5rem);
|
|
7793
|
+
block-size: var(--oc-avatar-picker-size, 4.5rem);
|
|
7794
|
+
overflow: hidden;
|
|
7795
|
+
font-size: 1.25rem;
|
|
7796
|
+
font-weight: 600;
|
|
7797
|
+
color: var(--oc-muted-foreground);
|
|
7798
|
+
background: var(--oc-muted);
|
|
7799
|
+
border: 1px dashed var(--oc-border-strong, var(--oc-border));
|
|
7800
|
+
border-radius: 50%;
|
|
7801
|
+
cursor: pointer;
|
|
7802
|
+
}
|
|
7803
|
+
.oc-avatar-picker--square .oc-avatar-picker__preview { border-radius: var(--oc-radius); }
|
|
7804
|
+
.oc-avatar-picker__preview img { inline-size: 100%; block-size: 100%; object-fit: cover; }
|
|
7805
|
+
.oc-avatar-picker--logo .oc-avatar-picker__preview img { object-fit: contain; padding: 0.375rem; background: var(--color-white-50); }
|
|
7806
|
+
.oc-avatar-picker__preview:has(img) { border-style: solid; }
|
|
7807
|
+
.oc-avatar-picker__preview:hover,
|
|
7808
|
+
.oc-avatar-picker.is-dragover .oc-avatar-picker__preview {
|
|
7809
|
+
color: var(--oc-primary);
|
|
7810
|
+
background: oklch(from var(--oc-primary) l c h / 0.08);
|
|
7811
|
+
border-color: var(--oc-primary);
|
|
7812
|
+
}
|
|
7813
|
+
.oc-avatar-picker__preview:has(:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
|
|
7814
|
+
.oc-avatar-picker__file { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
|
|
7815
|
+
.oc-avatar-picker__text { display: grid; gap: 0.375rem; min-inline-size: 0; }
|
|
7816
|
+
.oc-avatar-picker__label { font-size: 0.875rem; font-weight: 600; }
|
|
7817
|
+
.oc-avatar-picker__hint { font-size: 0.75rem; color: var(--oc-muted-foreground); }
|
|
7818
|
+
.oc-avatar-picker__actions { display: flex; flex-wrap: wrap; gap: 0.375rem; }
|
|
7819
|
+
/* "Remove" only while there is an image. */
|
|
7820
|
+
.oc-avatar-picker:not(:has(.oc-avatar-picker__preview img)) [data-oc-avatar-remove] { display: none; }
|
|
7821
|
+
/* A refused file (wrong type or too large): red outline and the reason under the buttons. */
|
|
7822
|
+
.oc-avatar-picker.is-invalid .oc-avatar-picker__preview { border-color: var(--oc-destructive); }
|
|
7823
|
+
.oc-avatar-picker__error { font-size: 0.75rem; font-weight: 500; color: var(--oc-destructive); }
|
|
7824
|
+
|
|
7140
7825
|
/* parts/forms-barcode-input.css */
|
|
7141
7826
|
/* File: packages/ui/src/parts/forms-barcode-input.css
|
|
7142
7827
|
Map: wrapper (field + preview stacked) -> monospace field -> preview area -> placeholder bars -> code caption -> empty / invalid states
|
|
@@ -7406,6 +8091,61 @@ a.oc-tree-label:focus-visible { outline: 2px solid var(--oc-ring); outline-offse
|
|
|
7406
8091
|
/* Small swatches. */
|
|
7407
8092
|
.oc-color-select--sm .oc-color-select-swatch { inline-size: 1.25rem; block-size: 1.25rem; }
|
|
7408
8093
|
|
|
8094
|
+
/* parts/forms-date-field.css */
|
|
8095
|
+
/* File: packages/ui/src/parts/forms-date-field.css | Map: field frame -> icon zone -> native date input -> ISO week label -> sizes and states */
|
|
8096
|
+
/* Date field: calendar icon | line | date, with the ISO week at the end when wanted.
|
|
8097
|
+
The date is a native <input type="date"> (keyboard, picker and locale from the browser). The week label is filled
|
|
8098
|
+
by packages/ui/src/date-field.js (or by your own code). */
|
|
8099
|
+
|
|
8100
|
+
.oc-date-field {
|
|
8101
|
+
display: inline-flex;
|
|
8102
|
+
align-items: stretch;
|
|
8103
|
+
block-size: 2.5rem;
|
|
8104
|
+
background: var(--oc-background);
|
|
8105
|
+
border: 1px solid var(--oc-input, var(--oc-border));
|
|
8106
|
+
border-radius: var(--oc-radius);
|
|
8107
|
+
overflow: hidden;
|
|
8108
|
+
}
|
|
8109
|
+
.oc-date-field:focus-within { border-color: var(--oc-ring); box-shadow: 0 0 0 3px oklch(from var(--oc-ring) l c h / 0.18); }
|
|
8110
|
+
.oc-date-field__icon {
|
|
8111
|
+
display: inline-flex;
|
|
8112
|
+
align-items: center;
|
|
8113
|
+
justify-content: center;
|
|
8114
|
+
inline-size: 2.5rem;
|
|
8115
|
+
font-size: 1.125rem;
|
|
8116
|
+
color: var(--oc-muted-foreground);
|
|
8117
|
+
border-inline-end: 1px solid var(--oc-border);
|
|
8118
|
+
}
|
|
8119
|
+
.oc-date-field__input {
|
|
8120
|
+
min-inline-size: 0;
|
|
8121
|
+
padding: 0 0.75rem;
|
|
8122
|
+
font: inherit;
|
|
8123
|
+
font-size: 0.875rem;
|
|
8124
|
+
color: var(--oc-foreground);
|
|
8125
|
+
background: transparent;
|
|
8126
|
+
border: 0;
|
|
8127
|
+
outline: none;
|
|
8128
|
+
}
|
|
8129
|
+
.oc-date-field__input::-webkit-calendar-picker-indicator { opacity: 0.55; cursor: pointer; }
|
|
8130
|
+
/* ISO week: a quiet label at the end ("Week 45"); hidden while there is no date. */
|
|
8131
|
+
.oc-date-field__week {
|
|
8132
|
+
display: inline-flex;
|
|
8133
|
+
align-items: center;
|
|
8134
|
+
padding-inline: 0.75rem;
|
|
8135
|
+
font-size: 0.75rem;
|
|
8136
|
+
font-weight: 600;
|
|
8137
|
+
font-variant-numeric: tabular-nums;
|
|
8138
|
+
color: var(--oc-primary);
|
|
8139
|
+
background: oklch(from var(--oc-primary) l c h / 0.08);
|
|
8140
|
+
border-inline-start: 1px solid var(--oc-border);
|
|
8141
|
+
white-space: nowrap;
|
|
8142
|
+
}
|
|
8143
|
+
.oc-date-field__week:empty { display: none; }
|
|
8144
|
+
.oc-date-field--sm { block-size: 2rem; }
|
|
8145
|
+
.oc-date-field--sm .oc-date-field__icon { inline-size: 2rem; font-size: 1rem; }
|
|
8146
|
+
.oc-date-field:has(.oc-date-field__input:disabled) { opacity: 0.55; }
|
|
8147
|
+
.oc-date-field:has(.oc-date-field__input[aria-invalid="true"]) { border-color: var(--oc-destructive); }
|
|
8148
|
+
|
|
7409
8149
|
/* parts/forms-decimal-input.css */
|
|
7410
8150
|
/* File: packages/ui/src/parts/forms-decimal-input.css
|
|
7411
8151
|
Map: field (right-aligned tabular digits) -> unit suffix wrapper -> states -> --start alignment
|
|
@@ -7459,6 +8199,21 @@ a.oc-tree-label:focus-visible { outline: 2px solid var(--oc-ring); outline-offse
|
|
|
7459
8199
|
/* Disabled wrapper. */
|
|
7460
8200
|
.oc-decimal-field:has(.oc-decimal-input:disabled) { opacity: 0.5; cursor: not-allowed; }
|
|
7461
8201
|
|
|
8202
|
+
/* parts/forms-density.css */
|
|
8203
|
+
/* File: packages/ui/src/parts/forms-density.css | Map: compact scope -> inputs, selects, date fields, textareas -> buttons -> field gaps */
|
|
8204
|
+
/* Form density: .oc-density-compact on a form or any wrapper makes its controls
|
|
8205
|
+
2rem high instead of 2.5rem, for dense editors (invoice templates, inline tables). The default needs no class. */
|
|
8206
|
+
|
|
8207
|
+
.oc-density-compact :is(.oc-input, .oc-select, .oc-date-field) { block-size: 2rem; font-size: 0.8125rem; }
|
|
8208
|
+
.oc-density-compact :is(.oc-input, .oc-select) { padding-block: 0; }
|
|
8209
|
+
.oc-density-compact .oc-textarea { padding-block: 0.375rem; font-size: 0.8125rem; }
|
|
8210
|
+
.oc-density-compact .oc-date-field__icon { inline-size: 2rem; font-size: 1rem; }
|
|
8211
|
+
.oc-density-compact .oc-date-field__input { font-size: 0.8125rem; }
|
|
8212
|
+
.oc-density-compact .oc-btn:not(.oc-btn--icon, .oc-btn--plain, .oc-btn--inline) { padding-block: 0.3125rem; font-size: 0.8125rem; }
|
|
8213
|
+
.oc-density-compact .oc-btn--icon { --oc-btn-square: 2rem; }
|
|
8214
|
+
.oc-density-compact .oc-field { gap: 0.25rem; }
|
|
8215
|
+
.oc-density-compact .oc-field-label { font-size: 0.75rem; }
|
|
8216
|
+
|
|
7462
8217
|
/* parts/forms-filter-counts.css */
|
|
7463
8218
|
/* File: packages/ui/src/parts/forms-filter-counts.css
|
|
7464
8219
|
Map: group (grid of tiles) -> tile (label + hidden radio) -> value / label / hint -> tones -> states -> --compact
|
|
@@ -7555,38 +8310,182 @@ a.oc-tree-label:focus-visible { outline: 2px solid var(--oc-ring); outline-offse
|
|
|
7555
8310
|
.oc-filter-counts--compact .oc-filter-counts-label { font-size: 0.8rem; font-weight: 500; }
|
|
7556
8311
|
.oc-filter-counts--compact .oc-filter-counts-hint { display: none; }
|
|
7557
8312
|
|
|
7558
|
-
/* parts/forms-
|
|
7559
|
-
/* File: packages/ui/src/parts/forms-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
A native <select> inside a small frame with a leading icon or symbol. The app reacts to "change". */
|
|
8313
|
+
/* parts/forms-form-section.css */
|
|
8314
|
+
/* File: packages/ui/src/parts/forms-form-section.css | Map: section (details) -> summary (icon, title, hint, status, chevron) -> body grid -> invalid state */
|
|
8315
|
+
/* Collapsible form section: a long form in named parts that open and close.
|
|
8316
|
+
Native <details>; sections with the same name="" keep one open at a time. */
|
|
7563
8317
|
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
align-items: center;
|
|
7569
|
-
gap: 0.375rem;
|
|
7570
|
-
padding-inline: 0.625rem 0.5rem;
|
|
7571
|
-
border: 1px solid var(--oc-input);
|
|
7572
|
-
border-radius: 999px;
|
|
7573
|
-
color: var(--oc-foreground);
|
|
7574
|
-
font-size: 0.8rem;
|
|
7575
|
-
transition: border-color var(--oc-transition-fast), background var(--oc-transition-fast);
|
|
8318
|
+
.oc-form-section {
|
|
8319
|
+
background: var(--oc-card);
|
|
8320
|
+
border: 1px solid var(--oc-border);
|
|
8321
|
+
border-radius: var(--oc-radius);
|
|
7576
8322
|
}
|
|
7577
|
-
|
|
7578
|
-
.oc-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
8323
|
+
.oc-form-section + .oc-form-section { margin-block-start: 0.75rem; }
|
|
8324
|
+
.oc-form-section__summary {
|
|
8325
|
+
display: grid;
|
|
8326
|
+
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
|
8327
|
+
align-items: center;
|
|
8328
|
+
gap: 0.25rem 0.75rem;
|
|
8329
|
+
padding: 0.875rem 1rem;
|
|
8330
|
+
list-style: none;
|
|
8331
|
+
cursor: pointer;
|
|
7583
8332
|
}
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
.oc-
|
|
8333
|
+
.oc-form-section__summary::-webkit-details-marker { display: none; }
|
|
8334
|
+
.oc-form-section__summary:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; border-radius: var(--oc-radius); }
|
|
8335
|
+
.oc-form-section__icon {
|
|
7587
8336
|
display: inline-flex;
|
|
7588
|
-
|
|
7589
|
-
|
|
8337
|
+
align-items: center;
|
|
8338
|
+
justify-content: center;
|
|
8339
|
+
inline-size: 2.25rem;
|
|
8340
|
+
block-size: 2.25rem;
|
|
8341
|
+
font-size: 1.25rem;
|
|
8342
|
+
color: var(--oc-primary);
|
|
8343
|
+
background: oklch(from var(--oc-primary) l c h / 0.1);
|
|
8344
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
8345
|
+
}
|
|
8346
|
+
.oc-form-section__heading { display: grid; gap: 0.125rem; min-inline-size: 0; }
|
|
8347
|
+
.oc-form-section__title { margin: 0; font-size: 0.9375rem; font-weight: 600; }
|
|
8348
|
+
.oc-form-section__hint { margin: 0; font-size: 0.8125rem; color: var(--oc-muted-foreground); }
|
|
8349
|
+
.oc-form-section__status { font-size: 0.75rem; white-space: nowrap; }
|
|
8350
|
+
.oc-form-section__summary::after {
|
|
8351
|
+
content: "";
|
|
8352
|
+
inline-size: 1.125rem;
|
|
8353
|
+
block-size: 1.125rem;
|
|
8354
|
+
background: var(--oc-muted-foreground);
|
|
8355
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6l-6-6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
8356
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6l-6-6z'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
8357
|
+
transition: rotate var(--oc-transition-fast);
|
|
8358
|
+
}
|
|
8359
|
+
.oc-form-section[open] > .oc-form-section__summary::after { rotate: 180deg; }
|
|
8360
|
+
.oc-form-section__body {
|
|
8361
|
+
display: grid;
|
|
8362
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
|
|
8363
|
+
gap: 1rem;
|
|
8364
|
+
padding: 1rem;
|
|
8365
|
+
border-block-start: 1px solid var(--oc-border);
|
|
8366
|
+
}
|
|
8367
|
+
.oc-form-section__body > .oc-form-section__wide { grid-column: 1 / -1; }
|
|
8368
|
+
/* A section with an invalid field gets a red edge, also while closed, so an error is never hidden. */
|
|
8369
|
+
.oc-form-section:has(:user-invalid, [aria-invalid="true"]) { border-color: var(--oc-destructive); }
|
|
8370
|
+
.oc-form-section:has(:user-invalid, [aria-invalid="true"]) .oc-form-section__icon {
|
|
8371
|
+
color: var(--oc-destructive);
|
|
8372
|
+
background: oklch(from var(--oc-destructive) l c h / 0.1);
|
|
8373
|
+
}
|
|
8374
|
+
|
|
8375
|
+
@media (prefers-reduced-motion: reduce) {
|
|
8376
|
+
.oc-form-section__summary::after { transition: none; }
|
|
8377
|
+
}
|
|
8378
|
+
|
|
8379
|
+
/* parts/forms-form.css */
|
|
8380
|
+
/* File: packages/ui/src/parts/forms-form.css */
|
|
8381
|
+
/* Map: form grid -> label hint -> actions + note -> busy -> filled variant */
|
|
8382
|
+
/* A form as one grid of fields with one gap, an "(optional)" hint in a label, a row with the submit button and a short
|
|
8383
|
+
note, the busy state while it sends (aria-busy on the form), and .oc-form--filled: fields with a fill and a stronger
|
|
8384
|
+
border, for a form on a card, a photo or a dark band. All through the field tokens of .oc-input. */
|
|
8385
|
+
|
|
8386
|
+
.oc-form {
|
|
8387
|
+
display: grid;
|
|
8388
|
+
gap: var(--oc-form-gap, 1.1rem);
|
|
8389
|
+
}
|
|
8390
|
+
.oc-form .oc-field-group { gap: var(--oc-form-gap, 1.1rem); }
|
|
8391
|
+
.oc-form .oc-field {
|
|
8392
|
+
gap: 0.4rem;
|
|
8393
|
+
margin-bottom: 0;
|
|
8394
|
+
}
|
|
8395
|
+
|
|
8396
|
+
/* <label class="oc-field-label">Phone <span class="oc-field-label__hint">(optional)</span></label> */
|
|
8397
|
+
.oc-field-label__hint {
|
|
8398
|
+
color: var(--oc-muted-foreground);
|
|
8399
|
+
font-weight: 400;
|
|
8400
|
+
}
|
|
8401
|
+
|
|
8402
|
+
/* Submit button with a short note next to it (wraps under it on a narrow form). */
|
|
8403
|
+
.oc-form-actions {
|
|
8404
|
+
display: flex;
|
|
8405
|
+
flex-wrap: wrap;
|
|
8406
|
+
align-items: center;
|
|
8407
|
+
gap: 1rem 1.5rem;
|
|
8408
|
+
margin-block-start: 0.4rem;
|
|
8409
|
+
}
|
|
8410
|
+
.oc-form-note {
|
|
8411
|
+
flex: 1 1 16rem;
|
|
8412
|
+
margin: 0;
|
|
8413
|
+
color: var(--oc-muted-foreground);
|
|
8414
|
+
font-size: 0.85rem;
|
|
8415
|
+
}
|
|
8416
|
+
.oc-form-note a {
|
|
8417
|
+
color: var(--oc-primary);
|
|
8418
|
+
text-decoration: underline;
|
|
8419
|
+
text-underline-offset: 3px;
|
|
8420
|
+
}
|
|
8421
|
+
.oc-form-note a:hover { color: var(--oc-foreground); }
|
|
8422
|
+
|
|
8423
|
+
/* While the form sends (form aria-busy="true"): the submit button dims and shows a progress cursor. */
|
|
8424
|
+
.oc-form[aria-busy="true"] :is([type="submit"], button:not([type])) {
|
|
8425
|
+
opacity: 0.7;
|
|
8426
|
+
cursor: progress;
|
|
8427
|
+
}
|
|
8428
|
+
|
|
8429
|
+
/* Filled fields: a fill (dark in dark mode), the strong border, more room and a wider focus ring. */
|
|
8430
|
+
.oc-form--filled {
|
|
8431
|
+
--oc-input-bg: light-dark(oklch(from var(--oc-foreground) l c h / 0.03), oklch(0 0 0 / 0.25));
|
|
8432
|
+
--oc-input-border: var(--oc-border-strong);
|
|
8433
|
+
--oc-input-padding: 0.75rem 0.9rem;
|
|
8434
|
+
--oc-input-radius: var(--oc-radius-md, 0.75rem);
|
|
8435
|
+
--oc-input-size: 1rem;
|
|
8436
|
+
--oc-ring-width: 3px;
|
|
8437
|
+
--oc-textarea-min: 9rem;
|
|
8438
|
+
}
|
|
8439
|
+
.oc-form--filled .oc-field-label {
|
|
8440
|
+
font-size: 0.9rem;
|
|
8441
|
+
font-weight: 600;
|
|
8442
|
+
line-height: 1.4;
|
|
8443
|
+
}
|
|
8444
|
+
.oc-form--filled .oc-field-error { font-size: 0.85rem; }
|
|
8445
|
+
.oc-form--filled .oc-select option { background: var(--oc-card); }
|
|
8446
|
+
|
|
8447
|
+
/* Honeypot: a field for bots, off screen for people. Not display:none (some bots skip hidden fields). Give the wrapper
|
|
8448
|
+
aria-hidden="true" and the input tabindex="-1" autocomplete="off"; the server ignores a form where it is filled. */
|
|
8449
|
+
.oc-honeypot {
|
|
8450
|
+
position: absolute;
|
|
8451
|
+
inset-inline-start: -10000px;
|
|
8452
|
+
inline-size: 1px;
|
|
8453
|
+
block-size: 1px;
|
|
8454
|
+
overflow: hidden;
|
|
8455
|
+
}
|
|
8456
|
+
|
|
8457
|
+
/* parts/forms-language-select.css */
|
|
8458
|
+
/* File: packages/ui/src/parts/forms-language-select.css
|
|
8459
|
+
Map: wrapper (icon + native select) -> icon -> select control -> chevron -> states -> --ghost / --currency
|
|
8460
|
+
Language select: compact locale (or currency) switcher for a site header.
|
|
8461
|
+
A native <select> inside a small frame with a leading icon or symbol. The app reacts to "change". */
|
|
8462
|
+
|
|
8463
|
+
/* Wrapper: inline frame; the select is stretched over it so the whole pill is clickable. */
|
|
8464
|
+
.oc-language-select {
|
|
8465
|
+
position: relative;
|
|
8466
|
+
display: inline-flex;
|
|
8467
|
+
align-items: center;
|
|
8468
|
+
gap: 0.375rem;
|
|
8469
|
+
padding-inline: 0.625rem 0.5rem;
|
|
8470
|
+
border: 1px solid var(--oc-input);
|
|
8471
|
+
border-radius: 999px;
|
|
8472
|
+
color: var(--oc-foreground);
|
|
8473
|
+
font-size: 0.8rem;
|
|
8474
|
+
transition: border-color var(--oc-transition-fast), background var(--oc-transition-fast);
|
|
8475
|
+
}
|
|
8476
|
+
|
|
8477
|
+
.oc-language-select:hover { background: var(--oc-surface); }
|
|
8478
|
+
|
|
8479
|
+
.oc-language-select:focus-within {
|
|
8480
|
+
border-color: var(--oc-ring);
|
|
8481
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--oc-ring) 25%, transparent);
|
|
8482
|
+
}
|
|
8483
|
+
|
|
8484
|
+
/* Leading icon (inline SVG globe) or symbol (€, $). Decorative: aria-hidden="true". */
|
|
8485
|
+
.oc-language-select-icon {
|
|
8486
|
+
display: inline-flex;
|
|
8487
|
+
flex: none;
|
|
8488
|
+
inline-size: 1rem;
|
|
7590
8489
|
justify-content: center;
|
|
7591
8490
|
color: var(--oc-muted-foreground);
|
|
7592
8491
|
font-weight: 600;
|
|
@@ -7890,6 +8789,146 @@ button.oc-language-select:hover { background: var(--oc-surface); }
|
|
|
7890
8789
|
font-size: 0.8rem;
|
|
7891
8790
|
}
|
|
7892
8791
|
|
|
8792
|
+
/* parts/forms-quantity.css */
|
|
8793
|
+
/* File: packages/ui/src/parts/forms-quantity.css | Map: frame -> step buttons -> number input (no spinners) -> focus and disabled -> sizes */
|
|
8794
|
+
/* Quantity field: minus, a number input and plus in one frame. Without a script it is a normal number input
|
|
8795
|
+
(the browser's arrows and keyboard); form-extras.js makes the buttons step within min / max / step. */
|
|
8796
|
+
|
|
8797
|
+
.oc-quantity {
|
|
8798
|
+
display: inline-flex;
|
|
8799
|
+
inline-size: fit-content;
|
|
8800
|
+
justify-self: start;
|
|
8801
|
+
align-items: stretch;
|
|
8802
|
+
block-size: 2.25rem;
|
|
8803
|
+
background: var(--oc-background);
|
|
8804
|
+
border: 1px solid var(--oc-input, var(--oc-border));
|
|
8805
|
+
border-radius: var(--oc-radius);
|
|
8806
|
+
overflow: hidden;
|
|
8807
|
+
}
|
|
8808
|
+
.oc-quantity__step {
|
|
8809
|
+
display: inline-grid;
|
|
8810
|
+
place-items: center;
|
|
8811
|
+
flex-shrink: 0;
|
|
8812
|
+
inline-size: 2.25rem;
|
|
8813
|
+
padding: 0;
|
|
8814
|
+
font: inherit;
|
|
8815
|
+
color: var(--oc-muted-foreground);
|
|
8816
|
+
background: transparent;
|
|
8817
|
+
border: 0;
|
|
8818
|
+
cursor: pointer;
|
|
8819
|
+
}
|
|
8820
|
+
.oc-quantity__step > :is(svg, iconify-icon) { inline-size: 1rem; block-size: 1rem; font-size: 1rem; }
|
|
8821
|
+
.oc-quantity__step:hover { color: var(--oc-foreground); background: var(--oc-accent); }
|
|
8822
|
+
.oc-quantity__step:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
|
|
8823
|
+
.oc-quantity__step:disabled { opacity: 0.35; cursor: not-allowed; background: transparent; }
|
|
8824
|
+
.oc-quantity__input {
|
|
8825
|
+
inline-size: 3.25rem;
|
|
8826
|
+
min-inline-size: 0;
|
|
8827
|
+
padding: 0 0.25rem;
|
|
8828
|
+
font: inherit;
|
|
8829
|
+
font-size: 0.875rem;
|
|
8830
|
+
font-weight: 600;
|
|
8831
|
+
font-variant-numeric: tabular-nums;
|
|
8832
|
+
text-align: center;
|
|
8833
|
+
color: var(--oc-foreground);
|
|
8834
|
+
background: transparent;
|
|
8835
|
+
border: 0;
|
|
8836
|
+
border-inline: 1px solid var(--oc-border);
|
|
8837
|
+
-moz-appearance: textfield;
|
|
8838
|
+
appearance: textfield;
|
|
8839
|
+
}
|
|
8840
|
+
.oc-quantity__input::-webkit-outer-spin-button,
|
|
8841
|
+
.oc-quantity__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
|
8842
|
+
.oc-quantity__input:focus { outline: none; }
|
|
8843
|
+
.oc-quantity:has(.oc-quantity__input:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
8844
|
+
.oc-quantity:has(.oc-quantity__input:disabled) { opacity: 0.5; }
|
|
8845
|
+
.oc-quantity--sm { block-size: 2rem; }
|
|
8846
|
+
.oc-quantity--sm .oc-quantity__step { inline-size: 2rem; }
|
|
8847
|
+
.oc-quantity--sm .oc-quantity__input { inline-size: 2.75rem; font-size: 0.8125rem; }
|
|
8848
|
+
|
|
8849
|
+
/* parts/forms-rating.css */
|
|
8850
|
+
/* File: packages/ui/src/parts/forms-rating.css | Map: star mask -> rating (display) -> sizes -> summary line -> rating input (radios, hover preview) -> focus */
|
|
8851
|
+
/* Star rating without JavaScript. .oc-rating shows a score (--oc-rating, halves and decimals work); .oc-rating-input
|
|
8852
|
+
is five radio buttons drawn as stars: :has() fills up to the checked or hovered star. */
|
|
8853
|
+
|
|
8854
|
+
.oc-rating,
|
|
8855
|
+
.oc-rating-input {
|
|
8856
|
+
--oc-rating-size: 1.125rem;
|
|
8857
|
+
--oc-rating-color: var(--oc-warning);
|
|
8858
|
+
--oc-rating-empty: oklch(from var(--oc-muted-foreground) l c h / 0.28);
|
|
8859
|
+
--oc-rating-star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.62L12 2L9.19 8.62L2 9.24l5.45 4.73L5.82 21z'/%3E%3C/svg%3E");
|
|
8860
|
+
}
|
|
8861
|
+
|
|
8862
|
+
/* Display: one bar of five stars; the fill is the score out of --oc-rating-max (default 5). */
|
|
8863
|
+
.oc-rating {
|
|
8864
|
+
--oc-rating: 0;
|
|
8865
|
+
--oc-rating-max: 5;
|
|
8866
|
+
display: inline-block;
|
|
8867
|
+
flex-shrink: 0;
|
|
8868
|
+
inline-size: calc(var(--oc-rating-size) * var(--oc-rating-max));
|
|
8869
|
+
block-size: var(--oc-rating-size);
|
|
8870
|
+
vertical-align: middle;
|
|
8871
|
+
background: linear-gradient(90deg, var(--oc-rating-color) calc(var(--oc-rating) / var(--oc-rating-max) * 100%), var(--oc-rating-empty) 0);
|
|
8872
|
+
-webkit-mask: var(--oc-rating-star) left center / var(--oc-rating-size) var(--oc-rating-size) repeat-x;
|
|
8873
|
+
mask: var(--oc-rating-star) left center / var(--oc-rating-size) var(--oc-rating-size) repeat-x;
|
|
8874
|
+
}
|
|
8875
|
+
.oc-rating--sm, .oc-rating-input.oc-rating-input--sm { --oc-rating-size: 0.875rem; }
|
|
8876
|
+
.oc-rating--lg, .oc-rating-input.oc-rating-input--lg { --oc-rating-size: 1.5rem; }
|
|
8877
|
+
|
|
8878
|
+
/* Summary: stars, the score and the number of reviews on one line. */
|
|
8879
|
+
.oc-rating-summary { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; font-size: 0.875rem; }
|
|
8880
|
+
.oc-rating-summary__score { font-weight: 700; font-variant-numeric: tabular-nums; }
|
|
8881
|
+
.oc-rating-summary__count { color: var(--oc-muted-foreground); }
|
|
8882
|
+
a.oc-rating-summary__count { text-decoration: underline; text-underline-offset: 0.2em; }
|
|
8883
|
+
a.oc-rating-summary__count:hover { color: var(--oc-foreground); }
|
|
8884
|
+
|
|
8885
|
+
/* Input: a fieldset with five labels (radio inside). The stars are one masked bar behind the labels. */
|
|
8886
|
+
.oc-rating-input {
|
|
8887
|
+
position: relative;
|
|
8888
|
+
display: inline-flex;
|
|
8889
|
+
inline-size: calc(var(--oc-rating-size) * 5 + 0.5rem);
|
|
8890
|
+
block-size: calc(var(--oc-rating-size) + 0.5rem);
|
|
8891
|
+
margin: 0;
|
|
8892
|
+
padding: 0.25rem;
|
|
8893
|
+
border: 0;
|
|
8894
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
8895
|
+
}
|
|
8896
|
+
.oc-rating-input::before {
|
|
8897
|
+
content: "";
|
|
8898
|
+
position: absolute;
|
|
8899
|
+
inset: 0.25rem;
|
|
8900
|
+
background: linear-gradient(90deg, var(--oc-rating-color) calc(var(--oc-rating-hover, var(--oc-rating-pick, 0)) * var(--oc-rating-size)), var(--oc-rating-empty) 0);
|
|
8901
|
+
-webkit-mask: var(--oc-rating-star) left center / var(--oc-rating-size) var(--oc-rating-size) repeat-x;
|
|
8902
|
+
mask: var(--oc-rating-star) left center / var(--oc-rating-size) var(--oc-rating-size) repeat-x;
|
|
8903
|
+
pointer-events: none;
|
|
8904
|
+
}
|
|
8905
|
+
.oc-rating-input > legend { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
8906
|
+
.oc-rating-input > label { position: relative; z-index: 1; flex: 0 0 var(--oc-rating-size); cursor: pointer; }
|
|
8907
|
+
.oc-rating-input > label > input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
8908
|
+
.oc-rating-input > label > span { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
8909
|
+
|
|
8910
|
+
.oc-rating-input:has(label:nth-of-type(1) input:checked) { --oc-rating-pick: 1; }
|
|
8911
|
+
.oc-rating-input:has(label:nth-of-type(2) input:checked) { --oc-rating-pick: 2; }
|
|
8912
|
+
.oc-rating-input:has(label:nth-of-type(3) input:checked) { --oc-rating-pick: 3; }
|
|
8913
|
+
.oc-rating-input:has(label:nth-of-type(4) input:checked) { --oc-rating-pick: 4; }
|
|
8914
|
+
.oc-rating-input:has(label:nth-of-type(5) input:checked) { --oc-rating-pick: 5; }
|
|
8915
|
+
/* Hover preview (pointer devices only, so a tap does not leave a preview behind). */
|
|
8916
|
+
@media (hover: hover) {
|
|
8917
|
+
.oc-rating-input:has(label:nth-of-type(1):hover) { --oc-rating-hover: 1; }
|
|
8918
|
+
.oc-rating-input:has(label:nth-of-type(2):hover) { --oc-rating-hover: 2; }
|
|
8919
|
+
.oc-rating-input:has(label:nth-of-type(3):hover) { --oc-rating-hover: 3; }
|
|
8920
|
+
.oc-rating-input:has(label:nth-of-type(4):hover) { --oc-rating-hover: 4; }
|
|
8921
|
+
.oc-rating-input:has(label:nth-of-type(5):hover) { --oc-rating-hover: 5; }
|
|
8922
|
+
}
|
|
8923
|
+
.oc-rating-input:has(input:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
8924
|
+
.oc-rating-input:has(input:disabled) { opacity: 0.5; }
|
|
8925
|
+
.oc-rating-input:has(input:disabled) > label,
|
|
8926
|
+
.oc-rating-input:has(input:disabled) > label > input { cursor: not-allowed; }
|
|
8927
|
+
|
|
8928
|
+
@media (forced-colors: active) {
|
|
8929
|
+
.oc-rating, .oc-rating-input::before { --oc-rating-color: CanvasText; --oc-rating-empty: GrayText; forced-color-adjust: none; }
|
|
8930
|
+
}
|
|
8931
|
+
|
|
7893
8932
|
/* parts/forms-time-input.css */
|
|
7894
8933
|
/* File: packages/ui/src/parts/forms-time-input.css
|
|
7895
8934
|
Map: 24h text field (HH:mm) -> invalid state -> slot list (radio chips) -> slot states -> --sm
|
|
@@ -8064,7 +9103,7 @@ button.oc-language-select:hover { background: var(--oc-surface); }
|
|
|
8064
9103
|
/* File: packages/ui/src/parts/media-attachment.css | Map: list -> file chip (icon by type, name, size, open/remove) -> type tones
|
|
8065
9104
|
-> uploading progress + error -> image preview (in a message) -> thumbnail chip (in a composer) -> reduced motion
|
|
8066
9105
|
Attachment: a file chip or an image preview inside a chat message or a composer. The icon tile shows the extension and is
|
|
8067
|
-
tinted per type (--pdf, --doc, --sheet, --zip, --image).
|
|
9106
|
+
tinted per type (--pdf, --doc, --sheet, --zip, --image). */
|
|
8068
9107
|
|
|
8069
9108
|
.oc-attachment-list { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
|
|
8070
9109
|
.oc-attachment-list--stack { flex-direction: column; flex-wrap: nowrap; }
|
|
@@ -8222,6 +9261,31 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
8222
9261
|
.oc-attachment, .oc-attachment--uploading::after { transition: none; }
|
|
8223
9262
|
}
|
|
8224
9263
|
|
|
9264
|
+
/* parts/media-barcode.css */
|
|
9265
|
+
/* File: packages/ui/src/parts/media-barcode.css | Map: barcode figure -> svg bars -> digits -> sizes (hero, compact) -> error */
|
|
9266
|
+
/* Barcode display: EAN-13, EAN-8, UPC-A or Code 128, drawn as SVG by
|
|
9267
|
+
packages/ui/src/barcode.js from data-value. Always black bars on white, in both themes, so scanners read it. */
|
|
9268
|
+
|
|
9269
|
+
.oc-barcode {
|
|
9270
|
+
display: inline-flex;
|
|
9271
|
+
flex-direction: column;
|
|
9272
|
+
align-items: center;
|
|
9273
|
+
gap: 0.25rem;
|
|
9274
|
+
margin: 0;
|
|
9275
|
+
padding: 0.75rem 1rem;
|
|
9276
|
+
color: var(--color-black-50);
|
|
9277
|
+
background: var(--color-white-50);
|
|
9278
|
+
border: 1px solid var(--oc-border);
|
|
9279
|
+
border-radius: var(--oc-radius);
|
|
9280
|
+
}
|
|
9281
|
+
.oc-barcode svg { display: block; inline-size: auto; block-size: var(--oc-barcode-height, 4rem); max-inline-size: 100%; }
|
|
9282
|
+
.oc-barcode__digits { font-family: var(--oc-font-mono); font-size: 0.8125rem; letter-spacing: 0.18em; color: inherit; }
|
|
9283
|
+
.oc-barcode--hero { padding: 1.25rem 1.5rem; --oc-barcode-height: 6rem; }
|
|
9284
|
+
.oc-barcode--hero .oc-barcode__digits { font-size: 1rem; }
|
|
9285
|
+
.oc-barcode--compact { padding: 0.375rem 0.5rem; --oc-barcode-height: 2.25rem; }
|
|
9286
|
+
.oc-barcode--compact .oc-barcode__digits { font-size: 0.6875rem; letter-spacing: 0.1em; }
|
|
9287
|
+
.oc-barcode[data-error] .oc-barcode__digits { color: var(--color-red-70); letter-spacing: 0; }
|
|
9288
|
+
|
|
8225
9289
|
/* parts/media-browser-frame.css */
|
|
8226
9290
|
/* File: packages/ui/src/parts/media-browser-frame.css | Map: frame -> chrome bar (dots, address pill, actions)
|
|
8227
9291
|
-> view (screenshot slot, ratio) -> caption -> minimal / dark / tilt modifiers -> motion
|
|
@@ -8441,6 +9505,9 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
8441
9505
|
line-height: 1;
|
|
8442
9506
|
cursor: pointer;
|
|
8443
9507
|
}
|
|
9508
|
+
/* Webicons in the buttons (inline SVG or <iconify-icon>): a fixed size, so nothing is cut off at the round edge. */
|
|
9509
|
+
.oc-chat-composer__tool > :is(svg, iconify-icon) { inline-size: 1.25rem; block-size: 1.25rem; font-size: 1.25rem; }
|
|
9510
|
+
.oc-chat-composer__send > :is(svg, iconify-icon) { inline-size: 1.125rem; block-size: 1.125rem; font-size: 1.125rem; }
|
|
8444
9511
|
.oc-chat-composer__tool:hover { background: var(--oc-surface-strong); color: var(--oc-foreground); }
|
|
8445
9512
|
.oc-chat-composer__tool:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
8446
9513
|
|
|
@@ -8645,9 +9712,11 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
8645
9712
|
background: transparent;
|
|
8646
9713
|
font-size: 1.15rem;
|
|
8647
9714
|
line-height: 1;
|
|
9715
|
+
color: var(--oc-muted-foreground);
|
|
8648
9716
|
cursor: pointer;
|
|
8649
9717
|
}
|
|
8650
|
-
.oc-emoji-picker__trigger:
|
|
9718
|
+
.oc-emoji-picker__trigger > :is(svg, iconify-icon) { inline-size: 1.25rem; block-size: 1.25rem; font-size: 1.25rem; }
|
|
9719
|
+
.oc-emoji-picker__trigger:hover { color: var(--oc-foreground); background: var(--oc-surface-strong); }
|
|
8651
9720
|
.oc-emoji-picker__trigger:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
8652
9721
|
|
|
8653
9722
|
/* The panel: floats above the trigger (implicit anchor of popovertarget), flips when there is no room. */
|
|
@@ -8851,6 +9920,216 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
8851
9920
|
.oc-flip-card--hover:is(:hover, :focus-within) .oc-flip-card__face--back { opacity: 1; visibility: visible; }
|
|
8852
9921
|
}
|
|
8853
9922
|
|
|
9923
|
+
/* parts/media-gallery.css */
|
|
9924
|
+
/* File: packages/ui/src/parts/media-gallery.css | Map: gallery grid -> stage and slides (radio + :has, up to 8) -> badge -> thumbnails -> side layout (container query) */
|
|
9925
|
+
/* Product gallery without JavaScript: one large photo and a row of thumbnails. Every thumbnail is a label with a
|
|
9926
|
+
radio; :has() shows the slide at the same position. Arrow keys move between the photos (native radios). */
|
|
9927
|
+
|
|
9928
|
+
.oc-gallery { container-type: inline-size; inline-size: 100%; display: grid; gap: 0.75rem; min-inline-size: 0; }
|
|
9929
|
+
.oc-gallery__stage {
|
|
9930
|
+
position: relative;
|
|
9931
|
+
aspect-ratio: var(--oc-gallery-ratio, 4 / 3);
|
|
9932
|
+
overflow: hidden;
|
|
9933
|
+
background: var(--oc-muted);
|
|
9934
|
+
border: 1px solid var(--oc-border);
|
|
9935
|
+
border-radius: calc(var(--oc-radius) + 4px);
|
|
9936
|
+
}
|
|
9937
|
+
.oc-gallery__slide { display: none; margin: 0; block-size: 100%; }
|
|
9938
|
+
.oc-gallery__slide > img { display: block; inline-size: 100%; block-size: 100%; object-fit: var(--oc-gallery-fit, contain); }
|
|
9939
|
+
.oc-gallery:not(:has(.oc-gallery__thumb input:checked)) .oc-gallery__slide:first-of-type,
|
|
9940
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(1) input:checked) .oc-gallery__slide:nth-of-type(1),
|
|
9941
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(2) input:checked) .oc-gallery__slide:nth-of-type(2),
|
|
9942
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(3) input:checked) .oc-gallery__slide:nth-of-type(3),
|
|
9943
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(4) input:checked) .oc-gallery__slide:nth-of-type(4),
|
|
9944
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(5) input:checked) .oc-gallery__slide:nth-of-type(5),
|
|
9945
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(6) input:checked) .oc-gallery__slide:nth-of-type(6),
|
|
9946
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(7) input:checked) .oc-gallery__slide:nth-of-type(7),
|
|
9947
|
+
.oc-gallery:has(.oc-gallery__thumb:nth-of-type(8) input:checked) .oc-gallery__slide:nth-of-type(8) { display: block; }
|
|
9948
|
+
.oc-gallery__badge { position: absolute; inset: 0.75rem auto auto 0.75rem; z-index: 1; }
|
|
9949
|
+
|
|
9950
|
+
.oc-gallery__thumbs { display: flex; gap: 0.5rem; min-inline-size: 0; margin: 0; padding: 0.125rem; overflow-x: auto; border: 0; scrollbar-width: thin; }
|
|
9951
|
+
.oc-gallery__thumbs > legend { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
9952
|
+
.oc-gallery__thumb {
|
|
9953
|
+
position: relative;
|
|
9954
|
+
flex: 0 0 auto;
|
|
9955
|
+
inline-size: var(--oc-gallery-thumb, 4rem);
|
|
9956
|
+
aspect-ratio: 1;
|
|
9957
|
+
overflow: hidden;
|
|
9958
|
+
background: var(--oc-muted);
|
|
9959
|
+
border: 2px solid transparent;
|
|
9960
|
+
border-radius: var(--oc-radius);
|
|
9961
|
+
outline: 1px solid var(--oc-border);
|
|
9962
|
+
outline-offset: -1px;
|
|
9963
|
+
cursor: pointer;
|
|
9964
|
+
}
|
|
9965
|
+
.oc-gallery__thumb > img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
|
|
9966
|
+
.oc-gallery__thumb > input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
9967
|
+
.oc-gallery__thumb:hover { outline-color: var(--oc-muted-foreground); }
|
|
9968
|
+
.oc-gallery__thumb:has(input:checked) { border-color: var(--oc-primary); outline-color: transparent; }
|
|
9969
|
+
.oc-gallery__thumb:has(input:focus-visible) { box-shadow: 0 0 0 2px var(--oc-background), 0 0 0 4px var(--oc-ring); }
|
|
9970
|
+
|
|
9971
|
+
/* Side: thumbnails in a column on the left once the gallery itself is 32rem wide or more. The grid is always two
|
|
9972
|
+
tracks; below 32rem both children span them, so the query only moves the children (a container cannot query itself). */
|
|
9973
|
+
.oc-gallery--side { grid-template-columns: auto minmax(0, 1fr); }
|
|
9974
|
+
.oc-gallery--side > * { grid-column: 1 / -1; }
|
|
9975
|
+
@container (min-width: 32rem) {
|
|
9976
|
+
.oc-gallery--side > .oc-gallery__stage { grid-column: 2; grid-row: 1; }
|
|
9977
|
+
.oc-gallery--side > .oc-gallery__thumbs { grid-column: 1; grid-row: 1; flex-direction: column; overflow: visible; }
|
|
9978
|
+
}
|
|
9979
|
+
|
|
9980
|
+
/* parts/media-glass-liquid.css */
|
|
9981
|
+
/* File: packages/ui/src/parts/media-glass-liquid.css
|
|
9982
|
+
Map: liquid sheet (.oc-glass--liquid: clear tint, strong blur + saturation, inner light, specular rim) -> menu bar
|
|
9983
|
+
(.oc-glass-bar: items with icon and label, the current one in a raised bubble) -> icon-only and round button -> fallbacks
|
|
9984
|
+
Liquid glass in the style of recent desktop and phone systems, in CSS only: the backdrop is blurred and brightened,
|
|
9985
|
+
the edge catches light. Put it over a photo or colourful content; on a plain page it looks like a soft card. */
|
|
9986
|
+
|
|
9987
|
+
.oc-glass.oc-glass--liquid,
|
|
9988
|
+
.oc-glass--liquid,
|
|
9989
|
+
.oc-glass-bar,
|
|
9990
|
+
.oc-site-header--glass .oc-site-header__inner {
|
|
9991
|
+
--oc-liquid-tint: light-dark(oklch(100% 0 0deg / 0.1), oklch(100% 0 0deg / 0.05));
|
|
9992
|
+
--oc-liquid-edge: light-dark(oklch(100% 0 0deg / 0.55), oklch(100% 0 0deg / 0.22));
|
|
9993
|
+
--oc-liquid-blur: 5px;
|
|
9994
|
+
position: relative;
|
|
9995
|
+
isolation: isolate;
|
|
9996
|
+
background: var(--oc-liquid-tint);
|
|
9997
|
+
border: 1px solid var(--oc-liquid-edge);
|
|
9998
|
+
/* Light enters at the top left and leaves at the bottom right; a soft glow runs along the inner edge,
|
|
9999
|
+
so the sheet reads as a thick, clear lens rather than frosted plastic. */
|
|
10000
|
+
box-shadow:
|
|
10001
|
+
0 10px 30px -10px oklch(0% 0 0deg / 0.35),
|
|
10002
|
+
inset 2px 2px 1px -2px oklch(100% 0 0deg / 0.95),
|
|
10003
|
+
inset -2px -2px 1px -2px oklch(100% 0 0deg / 0.7),
|
|
10004
|
+
inset 0 0 10px 1px oklch(100% 0 0deg / 0.22),
|
|
10005
|
+
inset 0 -8px 16px -12px oklch(100% 0 0deg / 0.35);
|
|
10006
|
+
backdrop-filter: blur(var(--oc-liquid-blur)) saturate(1.9) brightness(1.1) contrast(1.05);
|
|
10007
|
+
}
|
|
10008
|
+
/* Specular rim: bright on the top-left and bottom-right edge, only on the border ring. */
|
|
10009
|
+
.oc-glass--liquid::before,
|
|
10010
|
+
.oc-glass-bar::before,
|
|
10011
|
+
.oc-site-header--glass .oc-site-header__inner::before {
|
|
10012
|
+
content: "";
|
|
10013
|
+
position: absolute;
|
|
10014
|
+
inset: -1px;
|
|
10015
|
+
z-index: 1;
|
|
10016
|
+
padding: 1.5px;
|
|
10017
|
+
border-radius: inherit;
|
|
10018
|
+
background: linear-gradient(135deg, oklch(100% 0 0deg / 1), oklch(100% 0 0deg / 0.15) 25%, oklch(100% 0 0deg / 0) 50%, oklch(100% 0 0deg / 0.15) 75%, oklch(100% 0 0deg / 0.8));
|
|
10019
|
+
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
10020
|
+
mask-composite: exclude;
|
|
10021
|
+
pointer-events: none;
|
|
10022
|
+
}
|
|
10023
|
+
|
|
10024
|
+
/* Menu bar: a pill of items, each an icon over a short label. */
|
|
10025
|
+
.oc-glass-bar {
|
|
10026
|
+
display: inline-flex;
|
|
10027
|
+
align-items: center;
|
|
10028
|
+
gap: 0.2rem;
|
|
10029
|
+
padding: 0.35rem;
|
|
10030
|
+
border-radius: 999px;
|
|
10031
|
+
}
|
|
10032
|
+
.oc-glass-bar__item {
|
|
10033
|
+
position: relative;
|
|
10034
|
+
display: grid;
|
|
10035
|
+
justify-items: center;
|
|
10036
|
+
gap: 0.15rem;
|
|
10037
|
+
min-inline-size: 4.25rem;
|
|
10038
|
+
padding: 0.45rem 0.85rem;
|
|
10039
|
+
border: 0;
|
|
10040
|
+
border-radius: 999px;
|
|
10041
|
+
color: light-dark(oklch(22% 0.02 260deg / 0.78), oklch(100% 0 0deg / 0.82));
|
|
10042
|
+
background: none;
|
|
10043
|
+
font: inherit;
|
|
10044
|
+
font-size: 0.7rem;
|
|
10045
|
+
font-weight: 600;
|
|
10046
|
+
line-height: 1.1;
|
|
10047
|
+
text-decoration: none;
|
|
10048
|
+
cursor: pointer;
|
|
10049
|
+
transition: background-color 0.25s, color 0.25s, box-shadow 0.25s, scale 0.2s;
|
|
10050
|
+
}
|
|
10051
|
+
.oc-glass-bar__item > :is(svg, iconify-icon) { inline-size: 1.4rem; block-size: 1.4rem; font-size: 1.4rem; }
|
|
10052
|
+
.oc-glass-bar__item:hover { background: light-dark(oklch(100% 0 0deg / 0.28), oklch(100% 0 0deg / 0.08)); }
|
|
10053
|
+
.oc-glass-bar__item:active { scale: 0.94; }
|
|
10054
|
+
.oc-glass-bar__item:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
10055
|
+
/* The current item sits in a raised, lighter bubble. */
|
|
10056
|
+
.oc-glass-bar__item:is([aria-current="page"], [aria-pressed="true"], :has(> input:checked)) {
|
|
10057
|
+
color: light-dark(var(--oc-primary), var(--color-white-50));
|
|
10058
|
+
background: light-dark(oklch(100% 0 0deg / 0.62), oklch(100% 0 0deg / 0.24));
|
|
10059
|
+
box-shadow: inset 0 1px 1px oklch(100% 0 0deg / 0.85), 0 6px 14px -8px oklch(0% 0 0deg / 0.4);
|
|
10060
|
+
}
|
|
10061
|
+
.oc-glass-bar__item > input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
10062
|
+
.oc-glass-bar__item:has(> input:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
10063
|
+
|
|
10064
|
+
/* Icons only, and a separate round button next to the bar (search, compose). */
|
|
10065
|
+
.oc-glass-bar--icons .oc-glass-bar__item { min-inline-size: 0; padding: 0.6rem; }
|
|
10066
|
+
.oc-glass-bar--round { padding: 0.35rem; border-radius: 999px; }
|
|
10067
|
+
.oc-glass-bar--round .oc-glass-bar__item { min-inline-size: 0; padding: 0.6rem; }
|
|
10068
|
+
.oc-glass-bar__sep { align-self: stretch; inline-size: 1px; margin: 0.4rem 0.15rem; background: var(--oc-liquid-edge); }
|
|
10069
|
+
|
|
10070
|
+
/* Less transparency asked for, or no backdrop-filter: a solid bar that still reads as one. */
|
|
10071
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
10072
|
+
.oc-glass--liquid, .oc-glass-bar { --oc-liquid-tint: var(--oc-card); backdrop-filter: none; }
|
|
10073
|
+
}
|
|
10074
|
+
@supports not (backdrop-filter: blur(1px)) {
|
|
10075
|
+
.oc-glass--liquid, .oc-glass-bar { --oc-liquid-tint: light-dark(oklch(100% 0 0deg / 0.85), oklch(25% 0.02 260deg / 0.9)); }
|
|
10076
|
+
}
|
|
10077
|
+
@media (prefers-reduced-motion: reduce) {
|
|
10078
|
+
.oc-glass-bar__item { transition: none; }
|
|
10079
|
+
}
|
|
10080
|
+
|
|
10081
|
+
/* parts/media-glass-options.css */
|
|
10082
|
+
/* File: packages/ui/src/parts/media-glass-options.css
|
|
10083
|
+
Map: amount (--clear: more see-through, --frost: more frosted) -> light content on a colourful backdrop -> stage (moving colour blobs to show it off)
|
|
10084
|
+
Extras for .oc-glass, .oc-glass--liquid and .oc-glass-bar (media-glass.css, media-glass-liquid.css). The selectors
|
|
10085
|
+
carry two classes so they win over the base glass whatever order the parts load in. */
|
|
10086
|
+
|
|
10087
|
+
/* Amount of glass: --clear lets more of the backdrop through, --frost hides more of it. */
|
|
10088
|
+
:is(.oc-glass, .oc-glass-bar).oc-glass--clear {
|
|
10089
|
+
--oc-glass-sheet: light-dark(oklch(from var(--oc-background) l c h / 0.32), oklch(from var(--oc-background) l c h / 0.26));
|
|
10090
|
+
--oc-glass-blur: 10px;
|
|
10091
|
+
--oc-liquid-tint: light-dark(oklch(100% 0 0deg / 0.05), oklch(100% 0 0deg / 0.03));
|
|
10092
|
+
--oc-liquid-blur: 2px;
|
|
10093
|
+
}
|
|
10094
|
+
:is(.oc-glass, .oc-glass-bar).oc-glass--frost {
|
|
10095
|
+
--oc-glass-sheet: light-dark(oklch(from var(--oc-background) l c h / 0.86), oklch(from var(--oc-background) l c h / 0.8));
|
|
10096
|
+
--oc-glass-blur: 40px;
|
|
10097
|
+
--oc-liquid-tint: light-dark(oklch(100% 0 0deg / 0.38), oklch(100% 0 0deg / 0.12));
|
|
10098
|
+
--oc-liquid-blur: 18px;
|
|
10099
|
+
}
|
|
10100
|
+
|
|
10101
|
+
|
|
10102
|
+
/* Light content: white icons and labels for glass over a dark or very colourful backdrop. */
|
|
10103
|
+
.oc-glass-bar.oc-glass-bar--light .oc-glass-bar__item {
|
|
10104
|
+
color: oklch(100% 0 0deg / 0.92);
|
|
10105
|
+
text-shadow: 0 1px 2px oklch(0% 0 0deg / 0.25);
|
|
10106
|
+
}
|
|
10107
|
+
.oc-glass-bar.oc-glass-bar--light .oc-glass-bar__item:is([aria-current="page"], [aria-pressed="true"], :has(> input:checked)) {
|
|
10108
|
+
color: var(--color-white-50);
|
|
10109
|
+
background: oklch(100% 0 0deg / 0.2);
|
|
10110
|
+
}
|
|
10111
|
+
.oc-glass-bar.oc-glass-bar--light .oc-glass-bar__item:hover { background: oklch(100% 0 0deg / 0.12); }
|
|
10112
|
+
|
|
10113
|
+
/* Stage: round colour shapes that drift slowly, so the change through the glass shows. A background for a hero or a demo. */
|
|
10114
|
+
.oc-glass-stage {
|
|
10115
|
+
position: relative;
|
|
10116
|
+
overflow: hidden;
|
|
10117
|
+
background:
|
|
10118
|
+
radial-gradient(circle at 22% 72%, var(--color-magenta-50) 0 16%, transparent 16.5%),
|
|
10119
|
+
radial-gradient(circle at 82% 68%, var(--color-orange-50) 0 18%, transparent 18.5%),
|
|
10120
|
+
radial-gradient(circle at 58% 18%, var(--color-skycyan-50) 0 13%, transparent 13.5%),
|
|
10121
|
+
radial-gradient(60% 70% at 30% 30%, var(--color-magenta-60), transparent 70%),
|
|
10122
|
+
linear-gradient(120deg, var(--color-purple-60), var(--color-violet-80));
|
|
10123
|
+
background-size: 140% 140%;
|
|
10124
|
+
animation: oc-glass-stage 18s ease-in-out infinite alternate;
|
|
10125
|
+
}
|
|
10126
|
+
@keyframes oc-glass-stage {
|
|
10127
|
+
to { background-position: 100% 60%; }
|
|
10128
|
+
}
|
|
10129
|
+
@media (prefers-reduced-motion: reduce) {
|
|
10130
|
+
.oc-glass-stage { animation: none; }
|
|
10131
|
+
}
|
|
10132
|
+
|
|
8854
10133
|
/* parts/media-glass.css */
|
|
8855
10134
|
/* File: packages/ui/src/parts/media-glass.css
|
|
8856
10135
|
Map: tokens (light-dark) -> sheet (.oc-glass: see-through, blurred, light top edge, soft shadow) -> panel (.oc-glass-panel:
|
|
@@ -8994,6 +10273,54 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
8994
10273
|
.oc-image-zoom__canvas { transition: inline-size var(--oc-transition-normal); }
|
|
8995
10274
|
}
|
|
8996
10275
|
|
|
10276
|
+
/* parts/media-lazy.css */
|
|
10277
|
+
/* File: packages/ui/src/parts/media-lazy.css | Map: box (aspect ratio, shimmer placeholder or a tiny blurred preview) -> image fades in -> error -> lazy sections
|
|
10278
|
+
Lazy loading. .oc-lazy: an image box that keeps its place, shows a shimmer (or --oc-lazy-preview, a tiny blurred
|
|
10279
|
+
copy) until the photo is there and then fades it in (lazy.js adds .is-loaded). .oc-lazy-section: content the browser
|
|
10280
|
+
only lays out and draws when it comes near the screen (content-visibility). */
|
|
10281
|
+
|
|
10282
|
+
.oc-lazy {
|
|
10283
|
+
--oc-lazy-ratio: 16 / 10;
|
|
10284
|
+
position: relative;
|
|
10285
|
+
display: block;
|
|
10286
|
+
margin: 0;
|
|
10287
|
+
overflow: hidden;
|
|
10288
|
+
aspect-ratio: var(--oc-lazy-ratio);
|
|
10289
|
+
border-radius: var(--oc-radius);
|
|
10290
|
+
background:
|
|
10291
|
+
linear-gradient(100deg, transparent 30%, oklch(from var(--oc-background) l c h / 0.55) 50%, transparent 70%) -150% 0 / 60% 100% no-repeat,
|
|
10292
|
+
var(--oc-muted);
|
|
10293
|
+
animation: oc-lazy-shimmer 1.4s ease-in-out infinite;
|
|
10294
|
+
}
|
|
10295
|
+
/* A tiny blurred preview instead of the shimmer: style="--oc-lazy-preview: url(photo-20px.webp)". */
|
|
10296
|
+
.oc-lazy[style*="--oc-lazy-preview"] { animation: none; background: var(--oc-muted); }
|
|
10297
|
+
.oc-lazy[style*="--oc-lazy-preview"]::before { content: ""; position: absolute; inset: 0; background: var(--oc-lazy-preview) center / cover no-repeat; filter: blur(14px); scale: 1.15; }
|
|
10298
|
+
.oc-lazy > img { position: relative; display: block; inline-size: 100%; block-size: 100%; object-fit: cover; opacity: 0; transition: opacity 0.45s ease; }
|
|
10299
|
+
.oc-lazy.is-loaded { animation: none; background: none; }
|
|
10300
|
+
.oc-lazy.is-loaded::before { display: none; }
|
|
10301
|
+
.oc-lazy.is-loaded > img { opacity: 1; }
|
|
10302
|
+
.oc-lazy.is-error { animation: none; }
|
|
10303
|
+
.oc-lazy.is-error > img { opacity: 0; }
|
|
10304
|
+
.oc-lazy > figcaption { position: absolute; inset: auto 0 0; padding: 0.4rem 0.6rem; font-size: 0.75rem; color: var(--color-white-50); background: linear-gradient(transparent, oklch(0% 0 0deg / 0.55)); }
|
|
10305
|
+
/* Without the script (or when it is off) the photo shows as soon as it is there. */
|
|
10306
|
+
@media (scripting: none) { .oc-lazy > img { opacity: 1; } }
|
|
10307
|
+
@keyframes oc-lazy-shimmer { to { background-position: 250% 0, 0 0; } }
|
|
10308
|
+
|
|
10309
|
+
/* Sections far down a long page: laid out and drawn only when they come near the screen. */
|
|
10310
|
+
.oc-lazy-section { content-visibility: auto; contain-intrinsic-size: auto var(--oc-lazy-size, 600px); }
|
|
10311
|
+
|
|
10312
|
+
@media (prefers-reduced-motion: reduce) {
|
|
10313
|
+
.oc-lazy { animation: none; }
|
|
10314
|
+
.oc-lazy > img { transition: none; }
|
|
10315
|
+
}
|
|
10316
|
+
|
|
10317
|
+
/* In a table or a list: a small square thumbnail, and placeholder rows (.oc-lazy-row) while the next page loads. */
|
|
10318
|
+
.oc-lazy.oc-lazy--thumb { --oc-lazy-ratio: 1; inline-size: 2.5rem; flex: none; border-radius: calc(var(--oc-radius) - 2px); }
|
|
10319
|
+
.oc-lazy-row > :is(td, th) { padding-block: 0.6rem; }
|
|
10320
|
+
.oc-lazy-row .oc-lazy-bar { display: block; block-size: 0.75rem; inline-size: var(--oc-lazy-bar, 70%); border-radius: 999px; background: var(--oc-muted); animation: oc-lazy-pulse 1.2s ease-in-out infinite alternate; }
|
|
10321
|
+
@keyframes oc-lazy-pulse { to { opacity: 0.45; } }
|
|
10322
|
+
@media (prefers-reduced-motion: reduce) { .oc-lazy-row .oc-lazy-bar { animation: none; } }
|
|
10323
|
+
|
|
8997
10324
|
/* parts/media-lightbox.css */
|
|
8998
10325
|
/* File: packages/ui/src/parts/media-lightbox.css | Map: thumbnails -> dialog + backdrop -> bar -> track + slides
|
|
8999
10326
|
-> nav buttons -> caption + counter -> single image -> small screens -> motion
|
|
@@ -9094,6 +10421,10 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
9094
10421
|
.oc-lightbox__nav { grid-row: 1; justify-self: center; }
|
|
9095
10422
|
.oc-lightbox__nav--prev { grid-column: 1; }
|
|
9096
10423
|
.oc-lightbox__nav--next { grid-column: 3; }
|
|
10424
|
+
.oc-lightbox__close :is(svg, iconify-icon, .oc-icon) { inline-size: 1.1rem; block-size: 1.1rem; font-size: 1.1rem; }
|
|
10425
|
+
/* A <picture> (animated WebP with a still image for "reduce motion") gets the frame; the image fits in it. */
|
|
10426
|
+
.oc-lightbox__media > picture { display: grid; overflow: hidden; }
|
|
10427
|
+
.oc-lightbox__media > picture > img { max-inline-size: 100%; max-block-size: calc(100dvh - 9rem); object-fit: contain; }
|
|
9097
10428
|
|
|
9098
10429
|
/* ---- Caption with the "2 / 5" counter ---- */
|
|
9099
10430
|
.oc-lightbox__caption {
|
|
@@ -9172,7 +10503,7 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
9172
10503
|
people, status, actions) -> wallpaper (masked doodles in a token color) -> scroll area (starts at the bottom)
|
|
9173
10504
|
-> composer footer -> sending overlay (aria-busy) -> reduced motion
|
|
9174
10505
|
Message thread: one conversation. The scroll area uses flex-direction: column-reverse, so it opens at the newest message
|
|
9175
|
-
and stays there when messages are added, without a script.
|
|
10506
|
+
and stays there when messages are added, without a script. */
|
|
9176
10507
|
|
|
9177
10508
|
.oc-message-thread {
|
|
9178
10509
|
--oc-message-thread-height: 32rem;
|
|
@@ -9310,7 +10641,7 @@ a.oc-attachment-image:hover > :is(img, .oc-media-art) { filter: brightness(1.05)
|
|
|
9310
10641
|
-> grouping (:has) -> hover actions -> date divider + system line -> reduced motion
|
|
9311
10642
|
Message: one chat bubble. Incoming on the left with the avatar, outgoing on the right in the primary tint.
|
|
9312
10643
|
Consecutive messages of one sender group by themselves: the avatar shows once (on the last one), the name once (on the
|
|
9313
|
-
first one).
|
|
10644
|
+
first one). */
|
|
9314
10645
|
|
|
9315
10646
|
.oc-message-list {
|
|
9316
10647
|
--oc-message-in: var(--oc-card);
|
|
@@ -9880,6 +11211,382 @@ a.oc-message__quote:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
9880
11211
|
.oc-watermark { --oc-watermark-opacity: 0.2; print-color-adjust: exact; }
|
|
9881
11212
|
}
|
|
9882
11213
|
|
|
11214
|
+
/* parts/nav-topbar-variants.css */
|
|
11215
|
+
/* File: packages/ui/src/parts/nav-topbar-variants.css
|
|
11216
|
+
Map: brand (logo tile, room before the menu) -> centred menu -> plain links -> dark / brand colour -> floating pill
|
|
11217
|
+
-> strip above the bar -> footer bar (split, links, dark)
|
|
11218
|
+
Variants of the site header (.oc-topbar in docs-ui.css) and the footer bar (.oc-footerbar). Colours are tokens. */
|
|
11219
|
+
|
|
11220
|
+
/* Brand: a logo tile and the name, always with room before the menu. */
|
|
11221
|
+
.oc-topbar-brand { display: inline-flex; align-items: center; gap: 0.55rem; margin-inline-end: 1rem; }
|
|
11222
|
+
.oc-topbar-logo {
|
|
11223
|
+
display: grid;
|
|
11224
|
+
place-items: center;
|
|
11225
|
+
flex: none;
|
|
11226
|
+
inline-size: 1.85rem;
|
|
11227
|
+
block-size: 1.85rem;
|
|
11228
|
+
border-radius: 9px;
|
|
11229
|
+
color: var(--color-white-50);
|
|
11230
|
+
background: linear-gradient(135deg, var(--oc-accent-from, var(--oc-primary)), var(--oc-accent-to, var(--oc-primary)));
|
|
11231
|
+
font-size: 0.95rem;
|
|
11232
|
+
}
|
|
11233
|
+
.oc-topbar-logo > :is(svg, iconify-icon) { inline-size: 1.1rem; block-size: 1.1rem; font-size: 1.1rem; }
|
|
11234
|
+
.oc-topbar-nav { min-inline-size: 0; }
|
|
11235
|
+
.oc-topbar-actions .oc-btn { white-space: nowrap; }
|
|
11236
|
+
|
|
11237
|
+
/* Centred menu: brand left, menu in the middle, actions right (from 761px; smaller screens keep the burger). */
|
|
11238
|
+
@media (min-width: 761px) {
|
|
11239
|
+
.oc-topbar--center { display: grid; grid-template-columns: 1fr auto 1fr; }
|
|
11240
|
+
.oc-topbar--center .oc-topbar-brand { justify-self: start; }
|
|
11241
|
+
.oc-topbar--center .oc-topbar-actions { justify-self: end; }
|
|
11242
|
+
}
|
|
11243
|
+
|
|
11244
|
+
/* Plain links: no pill frames, the current page underlined. */
|
|
11245
|
+
.oc-topbar--plain .oc-navmenu--site .oc-navmenu-link {
|
|
11246
|
+
border-color: transparent;
|
|
11247
|
+
border-radius: 0;
|
|
11248
|
+
background: none;
|
|
11249
|
+
color: var(--oc-muted-foreground);
|
|
11250
|
+
box-shadow: inset 0 -2px 0 transparent;
|
|
11251
|
+
}
|
|
11252
|
+
.oc-topbar--plain .oc-navmenu--site .oc-navmenu-link:hover { color: var(--oc-foreground); border-color: transparent; background: none; }
|
|
11253
|
+
.oc-topbar--plain .oc-navmenu--site .oc-navmenu-link[aria-current="page"] {
|
|
11254
|
+
color: var(--oc-foreground);
|
|
11255
|
+
border-color: transparent;
|
|
11256
|
+
background: none;
|
|
11257
|
+
box-shadow: inset 0 -2px 0 var(--oc-primary);
|
|
11258
|
+
}
|
|
11259
|
+
|
|
11260
|
+
/* Dark bar and brand-colour bar: light text, links as soft pills. */
|
|
11261
|
+
.oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) { color: var(--color-white-50); border-block-end-color: transparent; }
|
|
11262
|
+
.oc-topbar.oc-topbar--dark { background: var(--oc-dark-band, var(--color-duskblue-95)); }
|
|
11263
|
+
.oc-topbar--brand { background: linear-gradient(100deg, var(--oc-accent-from, var(--oc-primary)), var(--oc-accent-to, var(--oc-primary))); }
|
|
11264
|
+
.oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) .oc-navmenu--site .oc-navmenu-link {
|
|
11265
|
+
color: oklch(from var(--color-white-50) l c h / 0.8);
|
|
11266
|
+
border-color: transparent;
|
|
11267
|
+
background: none;
|
|
11268
|
+
}
|
|
11269
|
+
.oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) .oc-navmenu--site .oc-navmenu-link:is(:hover, [aria-current="page"]) {
|
|
11270
|
+
color: var(--color-white-50);
|
|
11271
|
+
border-color: transparent;
|
|
11272
|
+
background: oklch(from var(--color-white-50) l c h / 0.14);
|
|
11273
|
+
}
|
|
11274
|
+
.oc-topbar--dark .oc-topbar-logo { background: oklch(from var(--color-white-50) l c h / 0.14); }
|
|
11275
|
+
.oc-topbar--brand .oc-topbar-logo { color: var(--oc-primary); background: var(--color-white-50); }
|
|
11276
|
+
.oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) .oc-topbar-burger { color: inherit; border-color: oklch(from var(--color-white-50) l c h / 0.3); }
|
|
11277
|
+
|
|
11278
|
+
/* Floating: a rounded bar with room around it and a soft shadow (over a hero or a photo). */
|
|
11279
|
+
.oc-topbar--floating {
|
|
11280
|
+
margin: 0.75rem;
|
|
11281
|
+
border: 1px solid var(--oc-border);
|
|
11282
|
+
border-radius: 16px;
|
|
11283
|
+
box-shadow: 0 14px 34px -18px oklch(from var(--color-black-60) l c h / 0.35);
|
|
11284
|
+
}
|
|
11285
|
+
|
|
11286
|
+
/* Strip: a small row above the bar (contact, language, account). */
|
|
11287
|
+
.oc-topbar-strip {
|
|
11288
|
+
display: flex;
|
|
11289
|
+
flex-wrap: wrap;
|
|
11290
|
+
align-items: center;
|
|
11291
|
+
justify-content: space-between;
|
|
11292
|
+
gap: 0.25rem 1rem;
|
|
11293
|
+
padding: 0.35rem clamp(14px, 2vw, 28px);
|
|
11294
|
+
font-size: 0.78rem;
|
|
11295
|
+
color: var(--oc-muted-foreground);
|
|
11296
|
+
background: var(--oc-muted);
|
|
11297
|
+
}
|
|
11298
|
+
.oc-topbar-strip a { color: inherit; text-decoration: none; }
|
|
11299
|
+
.oc-topbar-strip a:hover { color: var(--oc-foreground); }
|
|
11300
|
+
.oc-topbar-strip > span { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 1rem; }
|
|
11301
|
+
|
|
11302
|
+
/* Footer bar: text left and links right (--split), a link list, a dark bar. */
|
|
11303
|
+
.oc-footerbar--split { justify-content: space-between; gap: 1rem; text-align: start; }
|
|
11304
|
+
.oc-footerbar--tall { block-size: auto; min-block-size: var(--ocm-footer-height, 2.25rem); padding-block: 0.85rem; flex-wrap: wrap; }
|
|
11305
|
+
.oc-footerbar-links { display: inline-flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin: 0; padding: 0; list-style: none; }
|
|
11306
|
+
.oc-footerbar-links a { color: var(--oc-muted-foreground); text-decoration: none; }
|
|
11307
|
+
.oc-footerbar-links a:hover { color: var(--oc-foreground); }
|
|
11308
|
+
.oc-footerbar.oc-footerbar--dark { color: oklch(from var(--color-white-50) l c h / 0.7); background: var(--oc-dark-band, var(--color-duskblue-95)); border-block-start-color: transparent; }
|
|
11309
|
+
.oc-footerbar--dark :is(a, .oc-footerbar-links a) { color: oklch(from var(--color-white-50) l c h / 0.85); }
|
|
11310
|
+
.oc-footerbar--dark .oc-footerbar-links a:hover { color: var(--color-white-50); }
|
|
11311
|
+
|
|
11312
|
+
/* parts/site-base.css */
|
|
11313
|
+
/* File: packages/ui/src/parts/site-base.css */
|
|
11314
|
+
/* Map: skip link -> icon */
|
|
11315
|
+
/* Two small site helpers: a skip link for keyboard users (hidden above the page until it gets the focus) and a basic
|
|
11316
|
+
inline icon (svg or iconify-icon) that is as large as the text around it. */
|
|
11317
|
+
|
|
11318
|
+
/* <a class="oc-skip-link" href="#main">Skip to content</a> as the first element in <body>. */
|
|
11319
|
+
.oc-skip-link {
|
|
11320
|
+
position: absolute;
|
|
11321
|
+
inset-inline-start: 1rem;
|
|
11322
|
+
inset-block-start: 1rem;
|
|
11323
|
+
/* Fully above the edge, whatever its height; drops in on focus. */
|
|
11324
|
+
translate: 0 calc(-100% - 2rem);
|
|
11325
|
+
z-index: 100;
|
|
11326
|
+
padding: 0.6rem 1rem;
|
|
11327
|
+
border-radius: var(--oc-radius-md, 0.75rem);
|
|
11328
|
+
background: var(--oc-foreground);
|
|
11329
|
+
color: var(--oc-background);
|
|
11330
|
+
font-weight: 600;
|
|
11331
|
+
text-decoration: none;
|
|
11332
|
+
transition: translate var(--oc-transition-fast);
|
|
11333
|
+
}
|
|
11334
|
+
.oc-skip-link:focus { translate: none; }
|
|
11335
|
+
|
|
11336
|
+
/* Inline icon: 1.25em (1.1em in a button), never squeezed in a flex row. */
|
|
11337
|
+
.oc-icon {
|
|
11338
|
+
inline-size: 1.25em;
|
|
11339
|
+
block-size: 1.25em;
|
|
11340
|
+
flex-shrink: 0;
|
|
11341
|
+
}
|
|
11342
|
+
.oc-btn > .oc-icon {
|
|
11343
|
+
inline-size: 1.1em;
|
|
11344
|
+
block-size: 1.1em;
|
|
11345
|
+
}
|
|
11346
|
+
|
|
11347
|
+
/* parts/site-brand-mark.css */
|
|
11348
|
+
/* File: packages/ui/src/parts/site-brand-mark.css */
|
|
11349
|
+
/* Map: brand mark (icon + text) -> word (accent part in the brand gradient) -> tagline -> sizes */
|
|
11350
|
+
/* A logo built from an icon and text: the mark (svg or img) with a soft glow in the brand color, the word mark with one
|
|
11351
|
+
part in the brand gradient, and a small tagline under it. Sizes --sm and --lg (a footer, a hero or a map). */
|
|
11352
|
+
|
|
11353
|
+
.oc-brand-mark {
|
|
11354
|
+
--oc-brand-mark-icon: 2.25rem;
|
|
11355
|
+
--oc-brand-mark-word: 1.35rem;
|
|
11356
|
+
--oc-brand-mark-tagline: 0.6rem;
|
|
11357
|
+
display: inline-flex;
|
|
11358
|
+
align-items: center;
|
|
11359
|
+
gap: 0.6rem;
|
|
11360
|
+
color: var(--oc-foreground);
|
|
11361
|
+
text-decoration: none;
|
|
11362
|
+
}
|
|
11363
|
+
.oc-brand-mark__icon {
|
|
11364
|
+
flex: none;
|
|
11365
|
+
inline-size: var(--oc-brand-mark-icon);
|
|
11366
|
+
block-size: var(--oc-brand-mark-icon);
|
|
11367
|
+
filter: drop-shadow(0 6px 14px oklch(from var(--oc-brand-1) l c h / 0.35));
|
|
11368
|
+
}
|
|
11369
|
+
.oc-brand-mark__text {
|
|
11370
|
+
display: grid;
|
|
11371
|
+
line-height: 1;
|
|
11372
|
+
}
|
|
11373
|
+
.oc-brand-mark__word {
|
|
11374
|
+
font-family: var(--oc-font-display, inherit);
|
|
11375
|
+
font-size: var(--oc-brand-mark-word);
|
|
11376
|
+
font-weight: 800;
|
|
11377
|
+
letter-spacing: -0.04em;
|
|
11378
|
+
}
|
|
11379
|
+
.oc-brand-mark__accent {
|
|
11380
|
+
background: linear-gradient(90deg, var(--oc-brand-1), var(--oc-brand-2));
|
|
11381
|
+
-webkit-background-clip: text;
|
|
11382
|
+
background-clip: text;
|
|
11383
|
+
color: transparent;
|
|
11384
|
+
}
|
|
11385
|
+
.oc-brand-mark__tagline {
|
|
11386
|
+
margin-block-start: 0.3rem;
|
|
11387
|
+
color: var(--oc-subtle-foreground, var(--oc-muted-foreground));
|
|
11388
|
+
font-family: var(--oc-font-mono);
|
|
11389
|
+
font-size: var(--oc-brand-mark-tagline);
|
|
11390
|
+
font-weight: 600;
|
|
11391
|
+
letter-spacing: 0.16em;
|
|
11392
|
+
text-transform: uppercase;
|
|
11393
|
+
}
|
|
11394
|
+
.oc-brand-mark--sm {
|
|
11395
|
+
--oc-brand-mark-icon: 1.75rem;
|
|
11396
|
+
--oc-brand-mark-word: 1.1rem;
|
|
11397
|
+
--oc-brand-mark-tagline: 0.55rem;
|
|
11398
|
+
}
|
|
11399
|
+
.oc-brand-mark--lg {
|
|
11400
|
+
--oc-brand-mark-icon: 2.6rem;
|
|
11401
|
+
--oc-brand-mark-word: 1.6rem;
|
|
11402
|
+
--oc-brand-mark-tagline: 0.66rem;
|
|
11403
|
+
}
|
|
11404
|
+
|
|
11405
|
+
/* parts/site-cards.css */
|
|
11406
|
+
/* File: packages/ui/src/parts/site-cards.css */
|
|
11407
|
+
/* Map: feature card -> link card -> icon list (+ points) -> accent list + note -> badge row + accent tagline
|
|
11408
|
+
-> screenshot grid -> browser frame glow + mark */
|
|
11409
|
+
/* Cards and lists for the pages of a company site: services and features (icon tile + title + text), cards that are
|
|
11410
|
+
links, a list of icon rows in frames (or as points with a title), a list with markers in the accent color, a small
|
|
11411
|
+
note, a row of badges, a tagline in the accent color, a grid of screenshots that open in the lightbox, and a browser
|
|
11412
|
+
frame with a glow in the accent color and a product mark on it. */
|
|
11413
|
+
|
|
11414
|
+
/* Feature / service card: tile, title, text; a quiet hover. In an .oc-card-grid (--oc-card-min sets the column width). */
|
|
11415
|
+
.oc-feature-card {
|
|
11416
|
+
padding: 1.6rem;
|
|
11417
|
+
border: 1px solid var(--oc-border);
|
|
11418
|
+
border-radius: var(--oc-radius-lg, 1.25rem);
|
|
11419
|
+
background: var(--oc-surface);
|
|
11420
|
+
transition: border-color 0.3s, background 0.3s;
|
|
11421
|
+
}
|
|
11422
|
+
.oc-feature-card:hover {
|
|
11423
|
+
border-color: var(--oc-border-strong);
|
|
11424
|
+
background: var(--oc-surface-strong);
|
|
11425
|
+
}
|
|
11426
|
+
.oc-feature-card > .oc-icon-tile { margin-block-end: 1.1rem; }
|
|
11427
|
+
.oc-feature-card__title {
|
|
11428
|
+
margin: 0 0 0.6rem;
|
|
11429
|
+
font-family: var(--oc-font-display, inherit);
|
|
11430
|
+
font-size: 1.2rem;
|
|
11431
|
+
line-height: 1.25;
|
|
11432
|
+
}
|
|
11433
|
+
.oc-feature-card__text {
|
|
11434
|
+
margin: 0;
|
|
11435
|
+
color: var(--oc-muted-foreground);
|
|
11436
|
+
font-size: 0.95rem;
|
|
11437
|
+
}
|
|
11438
|
+
|
|
11439
|
+
/* Card that is a link (an agent, a tool, a document): tile, title, text and a "more" line; hover in the accent color. */
|
|
11440
|
+
.oc-link-card {
|
|
11441
|
+
display: grid;
|
|
11442
|
+
align-content: start;
|
|
11443
|
+
gap: 0.4rem;
|
|
11444
|
+
padding: 1.25rem;
|
|
11445
|
+
border: 1px solid var(--oc-border);
|
|
11446
|
+
border-radius: var(--oc-radius-lg, 1.25rem);
|
|
11447
|
+
background: var(--oc-surface);
|
|
11448
|
+
color: var(--oc-foreground);
|
|
11449
|
+
text-decoration: none;
|
|
11450
|
+
transition: border-color 0.3s, background 0.3s;
|
|
11451
|
+
}
|
|
11452
|
+
/* --oc-link-card-hover: the hover color alone (without an accent preset, so the tile keeps its own color). */
|
|
11453
|
+
.oc-link-card:hover {
|
|
11454
|
+
border-color: oklch(from var(--oc-link-card-hover, var(--oc-accent-from)) l c h / 0.5);
|
|
11455
|
+
background: var(--oc-surface-strong);
|
|
11456
|
+
}
|
|
11457
|
+
.oc-link-card:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
|
|
11458
|
+
.oc-link-card > .oc-icon-tile { margin-block-end: 0.4rem; }
|
|
11459
|
+
.oc-link-card__title { font-family: var(--oc-font-display, inherit); font-weight: 700; }
|
|
11460
|
+
.oc-link-card__text { color: var(--oc-muted-foreground); font-size: 0.93rem; }
|
|
11461
|
+
.oc-link-card__more {
|
|
11462
|
+
display: inline-flex;
|
|
11463
|
+
align-items: center;
|
|
11464
|
+
gap: 0.35rem;
|
|
11465
|
+
margin-block-start: 0.4rem;
|
|
11466
|
+
font-size: 0.9rem;
|
|
11467
|
+
font-weight: 600;
|
|
11468
|
+
}
|
|
11469
|
+
.oc-link-card__more::after { content: "→"; transition: translate 0.25s var(--oc-ease-out, ease); }
|
|
11470
|
+
.oc-link-card:hover .oc-link-card__more::after { translate: 4px 0; }
|
|
11471
|
+
|
|
11472
|
+
/* Icon list: rows with an icon in a frame. --points: an icon tile next to a title and a text, without frames. */
|
|
11473
|
+
.oc-icon-list {
|
|
11474
|
+
display: grid;
|
|
11475
|
+
gap: 0.75rem;
|
|
11476
|
+
margin: 0;
|
|
11477
|
+
padding: 0;
|
|
11478
|
+
list-style: none;
|
|
11479
|
+
}
|
|
11480
|
+
.oc-icon-list > li {
|
|
11481
|
+
display: flex;
|
|
11482
|
+
align-items: center;
|
|
11483
|
+
gap: 0.75rem;
|
|
11484
|
+
padding: 0.85rem 1rem;
|
|
11485
|
+
border: 1px solid var(--oc-border);
|
|
11486
|
+
border-radius: var(--oc-radius-md, 0.75rem);
|
|
11487
|
+
background: var(--oc-surface);
|
|
11488
|
+
}
|
|
11489
|
+
.oc-icon-list > li > :is(svg, iconify-icon, .oc-icon) {
|
|
11490
|
+
flex: none;
|
|
11491
|
+
inline-size: 1.25rem;
|
|
11492
|
+
block-size: 1.25rem;
|
|
11493
|
+
font-size: 1.25rem;
|
|
11494
|
+
color: var(--oc-primary);
|
|
11495
|
+
}
|
|
11496
|
+
.oc-icon-list--points { gap: 1rem; }
|
|
11497
|
+
.oc-icon-list--points > li {
|
|
11498
|
+
display: grid;
|
|
11499
|
+
grid-template-columns: auto 1fr;
|
|
11500
|
+
align-items: start;
|
|
11501
|
+
gap: 0.25rem 0.9rem;
|
|
11502
|
+
padding: 0;
|
|
11503
|
+
border: 0;
|
|
11504
|
+
background: none;
|
|
11505
|
+
}
|
|
11506
|
+
.oc-icon-list--points > li > .oc-icon-tile { grid-row: span 2; }
|
|
11507
|
+
.oc-icon-list__title { font-family: var(--oc-font-display, inherit); font-weight: 700; }
|
|
11508
|
+
.oc-icon-list__text { color: var(--oc-muted-foreground); font-size: 0.95rem; }
|
|
11509
|
+
|
|
11510
|
+
/* List with markers in the accent color, and a small note under a block. */
|
|
11511
|
+
.oc-list--accent {
|
|
11512
|
+
display: grid;
|
|
11513
|
+
gap: 0.6rem;
|
|
11514
|
+
margin: 0;
|
|
11515
|
+
padding-inline-start: 1.1rem;
|
|
11516
|
+
list-style: disc;
|
|
11517
|
+
color: var(--oc-muted-foreground);
|
|
11518
|
+
font-size: 0.95rem;
|
|
11519
|
+
}
|
|
11520
|
+
.oc-list--accent > li::marker { color: var(--oc-accent-from, var(--oc-primary)); }
|
|
11521
|
+
.oc-note {
|
|
11522
|
+
margin: 1.25rem 0 0;
|
|
11523
|
+
color: var(--oc-muted-foreground);
|
|
11524
|
+
font-size: 0.85rem;
|
|
11525
|
+
}
|
|
11526
|
+
.oc-note--md {
|
|
11527
|
+
max-inline-size: 60rem;
|
|
11528
|
+
font-size: 1rem;
|
|
11529
|
+
}
|
|
11530
|
+
|
|
11531
|
+
/* Row of badges or pills above a title, and a tagline in the accent color (lighter, so it reads on a dark page). */
|
|
11532
|
+
.oc-badge-row {
|
|
11533
|
+
display: flex;
|
|
11534
|
+
flex-wrap: wrap;
|
|
11535
|
+
gap: 0.5rem;
|
|
11536
|
+
margin-block-end: 1.25rem;
|
|
11537
|
+
}
|
|
11538
|
+
.oc-tagline--accent {
|
|
11539
|
+
margin: 0 0 1rem;
|
|
11540
|
+
color: light-dark(var(--oc-accent-from), color-mix(in oklch, var(--oc-accent-from) 55%, white));
|
|
11541
|
+
font-size: clamp(1.2rem, 2.5vw, 1.6rem);
|
|
11542
|
+
font-weight: 600;
|
|
11543
|
+
}
|
|
11544
|
+
|
|
11545
|
+
/* Screenshot grid: figures with the whole image visible (contain, 16:10 on the card color) and a caption;
|
|
11546
|
+
a link or a button around the image opens the lightbox
|
|
11547
|
+
(<button command="show-modal" commandfor="<lightbox id>">). A <picture> (animated WebP with a still image) works the same. */
|
|
11548
|
+
.oc-shot-grid {
|
|
11549
|
+
display: grid;
|
|
11550
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--oc-shot-min, 18rem)), 1fr));
|
|
11551
|
+
gap: 1.25rem;
|
|
11552
|
+
}
|
|
11553
|
+
.oc-shot {
|
|
11554
|
+
margin: 0;
|
|
11555
|
+
border: 1px solid var(--oc-border);
|
|
11556
|
+
border-radius: var(--oc-radius-lg, 1.25rem);
|
|
11557
|
+
overflow: hidden;
|
|
11558
|
+
background: var(--oc-surface);
|
|
11559
|
+
}
|
|
11560
|
+
.oc-shot > :is(a, button) { display: block; inline-size: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
|
|
11561
|
+
.oc-shot > :is(a, button):focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
|
|
11562
|
+
.oc-shot :is(picture) { display: block; }
|
|
11563
|
+
.oc-shot img {
|
|
11564
|
+
display: block;
|
|
11565
|
+
inline-size: 100%;
|
|
11566
|
+
aspect-ratio: var(--oc-shot-ratio, 16 / 10);
|
|
11567
|
+
object-fit: contain;
|
|
11568
|
+
background: var(--oc-card);
|
|
11569
|
+
}
|
|
11570
|
+
.oc-shot > figcaption {
|
|
11571
|
+
padding: 0.8rem 1rem;
|
|
11572
|
+
color: var(--oc-muted-foreground);
|
|
11573
|
+
font-size: 0.88rem;
|
|
11574
|
+
}
|
|
11575
|
+
|
|
11576
|
+
/* Browser frame with a shadow in the accent color and a product mark on the bottom end corner. */
|
|
11577
|
+
.oc-browser-frame--glow {
|
|
11578
|
+
position: relative;
|
|
11579
|
+
box-shadow: 0 40px 90px -40px color-mix(in srgb, var(--oc-accent-from) 70%, transparent);
|
|
11580
|
+
}
|
|
11581
|
+
.oc-browser-frame--glow :is(.oc-browser-frame__view, .oc-browser-frame__body) img { transition: scale 0.6s var(--oc-ease-out, ease); }
|
|
11582
|
+
.oc-browser-frame--glow:hover :is(.oc-browser-frame__view, .oc-browser-frame__body) img { scale: 1.02; }
|
|
11583
|
+
.oc-browser-frame__mark {
|
|
11584
|
+
position: absolute;
|
|
11585
|
+
inset-block-end: 1rem;
|
|
11586
|
+
inset-inline-end: 1rem;
|
|
11587
|
+
filter: drop-shadow(0 10px 20px oklch(0 0 0 / 0.6));
|
|
11588
|
+
}
|
|
11589
|
+
|
|
9883
11590
|
/* parts/site-fact-list.css */
|
|
9884
11591
|
/* File: packages/ui/src/parts/site-fact-list.css
|
|
9885
11592
|
Map: plain -> compact -> stacked -> accent values -> narrow screens
|
|
@@ -9907,6 +11614,191 @@ a.oc-message__quote:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
9907
11614
|
/* Very narrow containers: wrap instead of squeezing label and value together. */
|
|
9908
11615
|
.oc-fact-list > div { flex-wrap: wrap; }
|
|
9909
11616
|
|
|
11617
|
+
/* parts/site-fly.css */
|
|
11618
|
+
/* File: packages/ui/src/parts/site-fly.css */
|
|
11619
|
+
/* Map: fly in (only with fly.js) -> visible -> reduced motion -> product card badge -> code window tokens */
|
|
11620
|
+
/* Cards that fly in when they scroll into view: they start from the middle of their row (--oc-fly-x, set by fly.js),
|
|
11621
|
+
70px lower, smaller and tilted, and land in their place. Without fly.js (html.oc-fly-ready is missing) or with
|
|
11622
|
+
"reduce motion" everything simply stands in its place. Options: --oc-fly-step (offset per column), --oc-fly-stagger
|
|
11623
|
+
(delay per column) and data-oc-fly-repeat (fly out again when leaving the view). The lift on hover uses translate, so it never fights the
|
|
11624
|
+
transform of the fly-in. Also: a "New" badge in a product card name and short syntax classes for a code window. */
|
|
11625
|
+
|
|
11626
|
+
.oc-fly-ready .oc-reveal--fly {
|
|
11627
|
+
/* --oc-fly-delay: set per card by fly.js from --oc-fly-stagger (delay per column). */
|
|
11628
|
+
transition:
|
|
11629
|
+
opacity 0.6s var(--oc-ease-out, ease) var(--oc-fly-delay, 0s),
|
|
11630
|
+
transform 0.9s cubic-bezier(0.16, 1, 0.3, 1) var(--oc-fly-delay, 0s),
|
|
11631
|
+
border-color 0.35s;
|
|
11632
|
+
}
|
|
11633
|
+
.oc-fly-ready .oc-reveal--fly:not(.is-visible) {
|
|
11634
|
+
opacity: 0;
|
|
11635
|
+
transform: perspective(900px) translate3d(var(--oc-fly-x, 0), 70px, 0) scale(0.9) rotateX(14deg);
|
|
11636
|
+
}
|
|
11637
|
+
@media (prefers-reduced-motion: reduce) {
|
|
11638
|
+
.oc-fly-ready .oc-reveal--fly:not(.is-visible) { opacity: 1; transform: none; }
|
|
11639
|
+
}
|
|
11640
|
+
|
|
11641
|
+
/* "New" after the name of a product card: <h3 class="oc-product-card__name">Product 1 <span class="oc-product-card__badge oc-pill">New</span></h3> */
|
|
11642
|
+
.oc-product-card__badge {
|
|
11643
|
+
margin-inline-start: 0.4rem;
|
|
11644
|
+
vertical-align: middle;
|
|
11645
|
+
font-family: var(--oc-font-sans);
|
|
11646
|
+
font-size: 0.7rem;
|
|
11647
|
+
letter-spacing: 0.04em;
|
|
11648
|
+
text-transform: uppercase;
|
|
11649
|
+
}
|
|
11650
|
+
|
|
11651
|
+
/* Short syntax classes for hand-written code in an .oc-code-window: keyword, string, constant, function, muted.
|
|
11652
|
+
They take the colors of the window (--oc-syntax-*), so they follow light and dark mode and a theme can change them. */
|
|
11653
|
+
.oc-code-window .oc-tok-k { color: var(--oc-syntax-keyword); }
|
|
11654
|
+
.oc-code-window .oc-tok-s { color: var(--oc-syntax-string); }
|
|
11655
|
+
.oc-code-window .oc-tok-c { color: var(--oc-syntax-constant); }
|
|
11656
|
+
.oc-code-window .oc-tok-f { color: var(--oc-syntax-function); }
|
|
11657
|
+
.oc-code-window .oc-tok-m { color: var(--oc-syntax-comment); }
|
|
11658
|
+
|
|
11659
|
+
/* parts/site-header-variants.css */
|
|
11660
|
+
/* File: packages/ui/src/parts/site-header-variants.css
|
|
11661
|
+
Map: actions slot -> centred menu -> dark -> floating pill -> glass pill -> footer: centred, dark, newsletter, social
|
|
11662
|
+
Variants of the marketing header (.oc-site-header) and footer (.oc-site-footer) in showcase.css. The sticky header
|
|
11663
|
+
turns to glass while scrolling; the coloured variants keep their own background (no scroll animation). */
|
|
11664
|
+
|
|
11665
|
+
.oc-site-header__actions { display: flex; align-items: center; gap: 0.5rem; flex: none; }
|
|
11666
|
+
.oc-site-header__brand { white-space: nowrap; flex: none; }
|
|
11667
|
+
.oc-site-header__nav > :is(a, button):not(.oc-btn) { white-space: nowrap; }
|
|
11668
|
+
|
|
11669
|
+
/* Centred menu: brand left, menu in the middle, actions right. */
|
|
11670
|
+
@media (min-width: 861px) {
|
|
11671
|
+
.oc-site-header--center .oc-site-header__inner { display: grid; grid-template-columns: 1fr auto 1fr; }
|
|
11672
|
+
.oc-site-header--center .oc-site-header__brand { justify-self: start; }
|
|
11673
|
+
.oc-site-header--center .oc-site-header__actions { justify-self: end; }
|
|
11674
|
+
}
|
|
11675
|
+
|
|
11676
|
+
/* Dark: a solid dark bar with light text. */
|
|
11677
|
+
.oc-site-header--dark {
|
|
11678
|
+
animation: none;
|
|
11679
|
+
color: var(--color-white-50);
|
|
11680
|
+
background: var(--oc-dark-band, var(--color-duskblue-95));
|
|
11681
|
+
}
|
|
11682
|
+
.oc-site-header--dark :is(.oc-site-header__brand, .oc-site-header__nav a[aria-current="page"], .oc-site-header__nav a:not(.oc-btn):hover) { color: var(--color-white-50); }
|
|
11683
|
+
.oc-site-header--dark .oc-site-header__nav a:not(.oc-btn) { color: oklch(from var(--color-white-50) l c h / 0.72); }
|
|
11684
|
+
|
|
11685
|
+
/* Floating: the bar as a rounded card with room around it. */
|
|
11686
|
+
.oc-site-header:is(.oc-site-header--floating, .oc-site-header--glass) { animation: none; padding: 0.75rem; }
|
|
11687
|
+
.oc-site-header--floating .oc-site-header__inner {
|
|
11688
|
+
block-size: 4rem;
|
|
11689
|
+
padding-inline: 1rem 0.75rem;
|
|
11690
|
+
border: 1px solid var(--oc-border);
|
|
11691
|
+
border-radius: 18px;
|
|
11692
|
+
background: var(--oc-background);
|
|
11693
|
+
box-shadow: 0 16px 36px -22px oklch(from var(--color-black-60) l c h / 0.45);
|
|
11694
|
+
}
|
|
11695
|
+
|
|
11696
|
+
/* Glass: a clear liquid-glass pill over a photo or a hero (look in media-glass-liquid.css). */
|
|
11697
|
+
.oc-site-header--glass .oc-site-header__inner { block-size: 3.75rem; padding-inline: 1.1rem 0.6rem; border-radius: 999px; }
|
|
11698
|
+
.oc-site-header--glass :is(.oc-site-header__brand, .oc-site-header__nav a:not(.oc-btn)) { color: light-dark(var(--color-black-95), var(--color-white-50)); text-shadow: 0 1px 2px light-dark(oklch(100% 0 0deg / 0.5), oklch(0% 0 0deg / 0.4)); }
|
|
11699
|
+
|
|
11700
|
+
/* ---- Footer variants ---- */
|
|
11701
|
+
/* Centred: brand, one row of links, social icons and the bottom line, all centred. */
|
|
11702
|
+
.oc-site-footer--center { text-align: center; }
|
|
11703
|
+
.oc-site-footer--center :is(.oc-site-footer__about, .oc-site-footer__bottom) { justify-items: center; justify-content: center; }
|
|
11704
|
+
.oc-site-footer--center .oc-site-footer__about { display: grid; gap: 1rem; }
|
|
11705
|
+
.oc-site-footer--center .oc-site-footer__about p { margin: 0; max-inline-size: 34rem; }
|
|
11706
|
+
.oc-site-footer__row { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem 1.75rem; margin: 2rem 0 0; padding: 0; list-style: none; }
|
|
11707
|
+
.oc-site-footer__social { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
|
|
11708
|
+
.oc-site-footer--center .oc-site-footer__social { justify-content: center; margin-block-start: 1.5rem; }
|
|
11709
|
+
.oc-site-footer__social a {
|
|
11710
|
+
display: grid;
|
|
11711
|
+
place-items: center;
|
|
11712
|
+
inline-size: 2.25rem;
|
|
11713
|
+
block-size: 2.25rem;
|
|
11714
|
+
border: 1px solid var(--oc-border);
|
|
11715
|
+
border-radius: 999px;
|
|
11716
|
+
transition: border-color 0.2s, color 0.2s;
|
|
11717
|
+
}
|
|
11718
|
+
.oc-site-footer__social a:hover { border-color: var(--oc-primary); color: var(--oc-primary); }
|
|
11719
|
+
.oc-site-footer__social :is(svg, iconify-icon) { inline-size: 1.1rem; block-size: 1.1rem; font-size: 1.1rem; }
|
|
11720
|
+
|
|
11721
|
+
/* Dark: light text on a dark band, the same deep blue in light and dark mode (--oc-dark-band), no top border. */
|
|
11722
|
+
.oc-site-footer--dark { border-block-start: 0; color: oklch(from var(--color-white-50) l c h / 0.7); background: var(--oc-dark-band, var(--color-duskblue-95)); }
|
|
11723
|
+
.oc-site-footer--dark :is(.oc-site-footer__title, .oc-site-header__brand, a:hover) { color: var(--color-white-50); }
|
|
11724
|
+
.oc-site-footer--dark :is(.oc-site-footer__bottom, .oc-site-footer__social a) { border-color: oklch(from var(--color-white-50) l c h / 0.16); }
|
|
11725
|
+
|
|
11726
|
+
/* Newsletter: a sign-up field in the brand column. */
|
|
11727
|
+
.oc-site-footer__signup { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-block-start: 1.25rem; max-inline-size: 24rem; }
|
|
11728
|
+
.oc-site-footer__signup .oc-input { flex: 1 1 12rem; min-inline-size: 0; }
|
|
11729
|
+
.oc-site-footer--dark .oc-site-footer__signup .oc-input { color: var(--color-white-50); background: oklch(from var(--color-white-50) l c h / 0.08); border-color: oklch(from var(--color-white-50) l c h / 0.2); }
|
|
11730
|
+
|
|
11731
|
+
/* parts/site-layout.css */
|
|
11732
|
+
/* File: packages/ui/src/parts/site-layout.css */
|
|
11733
|
+
/* Map: page hero -> section head / more -> action row -> group title -> band -> meta line */
|
|
11734
|
+
/* Page building blocks for a marketing or company site, on top of the showcase classes (.oc-section-pad, .oc-display):
|
|
11735
|
+
the larger header of an inner page, a section head and a "see more" row, a row of buttons, a group title with a count,
|
|
11736
|
+
a full-width band (for a stat strip) and a meta line (version · date). */
|
|
11737
|
+
|
|
11738
|
+
/* Inner page header: more room on top, less below, and a larger display title. */
|
|
11739
|
+
.oc-section-pad--page { padding-block: clamp(4rem, 10vw, 7rem) clamp(2rem, 5vw, 3rem); }
|
|
11740
|
+
.oc-display--page {
|
|
11741
|
+
margin-block-end: 1.25rem;
|
|
11742
|
+
font-size: clamp(2.5rem, 6.5vw, 4.75rem);
|
|
11743
|
+
}
|
|
11744
|
+
|
|
11745
|
+
/* Head of a section (eyebrow, title, lead) and the row with a "see all" link under the section. */
|
|
11746
|
+
.oc-section-head {
|
|
11747
|
+
max-inline-size: 46rem;
|
|
11748
|
+
margin-block-end: 3rem;
|
|
11749
|
+
}
|
|
11750
|
+
.oc-section-head--center {
|
|
11751
|
+
margin-inline: auto;
|
|
11752
|
+
text-align: center;
|
|
11753
|
+
}
|
|
11754
|
+
.oc-section-more { margin-block-start: 2.5rem; }
|
|
11755
|
+
|
|
11756
|
+
/* Row of buttons under a text: wraps on a narrow screen. */
|
|
11757
|
+
.oc-actions {
|
|
11758
|
+
display: flex;
|
|
11759
|
+
flex-wrap: wrap;
|
|
11760
|
+
gap: 0.75rem;
|
|
11761
|
+
margin-block-start: 2rem;
|
|
11762
|
+
}
|
|
11763
|
+
.oc-actions--center { justify-content: center; }
|
|
11764
|
+
.oc-actions--end { justify-content: flex-end; }
|
|
11765
|
+
.oc-actions--tight { margin-block-start: 1rem; }
|
|
11766
|
+
|
|
11767
|
+
/* Title of a group of cards with the number of items in a pill: <h3 class="oc-group-title">Apps <span class="oc-group-title__count">8</span></h3> */
|
|
11768
|
+
.oc-group-title {
|
|
11769
|
+
display: flex;
|
|
11770
|
+
align-items: center;
|
|
11771
|
+
gap: 0.75rem;
|
|
11772
|
+
margin: 0 0 1.5rem;
|
|
11773
|
+
font-size: 1.4rem;
|
|
11774
|
+
}
|
|
11775
|
+
.oc-group-title__count {
|
|
11776
|
+
padding: 0.1rem 0.6rem;
|
|
11777
|
+
border-radius: 999px;
|
|
11778
|
+
background: var(--oc-surface-strong);
|
|
11779
|
+
color: var(--oc-muted-foreground);
|
|
11780
|
+
font-size: 0.85rem;
|
|
11781
|
+
font-weight: 500;
|
|
11782
|
+
}
|
|
11783
|
+
|
|
11784
|
+
/* Band over the full width of the page with a line above and below; the content stays in its .oc-container.
|
|
11785
|
+
Put a .oc-stat-strip--plain in it for a row of key figures. */
|
|
11786
|
+
.oc-band {
|
|
11787
|
+
border-block: 1px solid var(--oc-border);
|
|
11788
|
+
background: var(--oc-band-bg, var(--oc-surface));
|
|
11789
|
+
}
|
|
11790
|
+
|
|
11791
|
+
/* Meta line under a page title: version · date, author. */
|
|
11792
|
+
.oc-meta {
|
|
11793
|
+
margin: 0 0 1rem;
|
|
11794
|
+
color: var(--oc-subtle-foreground, var(--oc-muted-foreground));
|
|
11795
|
+
font-size: 0.9rem;
|
|
11796
|
+
}
|
|
11797
|
+
|
|
11798
|
+
/* Muted and subtle text from the theme tokens, for running text inside a panel, a spotlight or a split. */
|
|
11799
|
+
.oc-text-muted { color: var(--oc-muted-foreground); }
|
|
11800
|
+
.oc-text-subtle { color: var(--oc-subtle-foreground, var(--oc-muted-foreground)); }
|
|
11801
|
+
|
|
9910
11802
|
/* parts/site-link-arrow.css */
|
|
9911
11803
|
/* File: packages/ui/src/parts/site-link-arrow.css
|
|
9912
11804
|
Map: focus ring -> back arrow -> external arrow -> color modifiers -> sizes -> reduced motion
|
|
@@ -10112,6 +12004,37 @@ a.oc-message__quote:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
10112
12004
|
.oc-next-teaser__tagline::before { transition: none; }
|
|
10113
12005
|
}
|
|
10114
12006
|
|
|
12007
|
+
/* parts/site-panel.css */
|
|
12008
|
+
/* File: packages/ui/src/parts/site-panel.css */
|
|
12009
|
+
/* Map: panel -> glow (start / end / both) */
|
|
12010
|
+
/* A framed panel on the page (a form, a call to action, a feature with a list): card background, strong border, large
|
|
12011
|
+
radius, and room above it when a link jumps to it (#form). --glow adds a soft radial light in a corner:
|
|
12012
|
+
--oc-panel-glow-color (brand 1) and --oc-panel-glow-at (top left); --glow-end puts it top right, --glow-both adds a
|
|
12013
|
+
second light (brand 3) bottom right. */
|
|
12014
|
+
|
|
12015
|
+
.oc-panel {
|
|
12016
|
+
--oc-panel-glow-color: var(--oc-brand-1);
|
|
12017
|
+
--oc-panel-glow-at: 0% 0%;
|
|
12018
|
+
position: relative;
|
|
12019
|
+
padding: var(--oc-panel-padding, clamp(1.5rem, 4vw, 2.5rem));
|
|
12020
|
+
border: 1px solid var(--oc-border-strong);
|
|
12021
|
+
border-radius: calc(var(--oc-radius-lg, 1.25rem) * 1.2);
|
|
12022
|
+
background: var(--oc-card);
|
|
12023
|
+
scroll-margin-top: var(--oc-scroll-offset, 6rem);
|
|
12024
|
+
}
|
|
12025
|
+
.oc-panel--glow {
|
|
12026
|
+
background:
|
|
12027
|
+
radial-gradient(30rem circle at var(--oc-panel-glow-at), oklch(from var(--oc-panel-glow-color) l c h / 0.12), transparent 60%),
|
|
12028
|
+
var(--oc-card);
|
|
12029
|
+
}
|
|
12030
|
+
.oc-panel--glow-end { --oc-panel-glow-at: 100% 0%; }
|
|
12031
|
+
.oc-panel--glow-both {
|
|
12032
|
+
background:
|
|
12033
|
+
radial-gradient(40rem circle at 0% 0%, oklch(from var(--oc-brand-1) l c h / 0.18), transparent 55%),
|
|
12034
|
+
radial-gradient(40rem circle at 100% 100%, oklch(from var(--oc-brand-3) l c h / 0.2), transparent 55%),
|
|
12035
|
+
var(--oc-card);
|
|
12036
|
+
}
|
|
12037
|
+
|
|
10115
12038
|
/* parts/site-price-card.css */
|
|
10116
12039
|
/* File: packages/ui/src/parts/site-price-card.css
|
|
10117
12040
|
Map: card -> head + discount badge -> period toggle -> monthly / yearly / lifetime price (:has) -> features -> featured
|
|
@@ -10400,7 +12323,7 @@ a.oc-message__quote:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
10400
12323
|
-> sizes (--sm, --lg) -> full width
|
|
10401
12324
|
Prose: typography for long-form text from a CMS, blog or help page, where the HTML has no classes of its own.
|
|
10402
12325
|
Every rule is scoped with :where(), so a utility class on an element inside still wins. Spacing is in em, so --sm and --lg
|
|
10403
|
-
scale the whole text.
|
|
12326
|
+
scale the whole text. */
|
|
10404
12327
|
|
|
10405
12328
|
.oc-prose {
|
|
10406
12329
|
max-inline-size: 68ch;
|
|
@@ -10531,6 +12454,18 @@ a.oc-message__quote:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
10531
12454
|
@media (prefers-reduced-motion: reduce) {
|
|
10532
12455
|
.oc-prose :where(a) { transition: none; }
|
|
10533
12456
|
}
|
|
12457
|
+
|
|
12458
|
+
/* ---- Legal text (terms, privacy, cookies): narrower, muted body text in a slightly larger size, headings and strong
|
|
12459
|
+
in the foreground, disc lists with a gap, links in brand 1. A table in an .oc-table-wrap scrolls on a phone. ---- */
|
|
12460
|
+
.oc-prose--legal { max-inline-size: 46rem; color: var(--oc-muted-foreground); font-size: 1.0625rem; }
|
|
12461
|
+
.oc-prose--legal :where(h2) { margin-block-start: 2.5rem; font-size: 1.35rem; color: var(--oc-foreground); }
|
|
12462
|
+
.oc-prose--legal :where(h3) { margin-block-start: 1.5rem; font-size: 1.05rem; color: var(--oc-foreground); }
|
|
12463
|
+
.oc-prose--legal :where(strong, b) { color: var(--oc-foreground); }
|
|
12464
|
+
.oc-prose--legal :where(ul) { display: grid; gap: 0.4rem; padding-inline-start: 1.25rem; list-style: disc; }
|
|
12465
|
+
.oc-prose--legal :where(ul > li) { margin-block-start: 0; }
|
|
12466
|
+
.oc-prose--legal :where(a) { color: var(--oc-brand-1); font-weight: inherit; text-decoration-color: currentColor; text-underline-offset: 3px; }
|
|
12467
|
+
.oc-prose--legal :where(a:hover) { color: var(--oc-foreground); }
|
|
12468
|
+
.oc-prose--legal :where(table) { font-size: 0.93rem; }
|
|
10534
12469
|
|
|
10535
12470
|
/* parts/site-stack-list.css */
|
|
10536
12471
|
/* File: packages/ui/src/parts/site-stack-list.css
|
|
@@ -10638,6 +12573,52 @@ a.oc-message__quote:focus-visible { outline: 2px solid var(--oc-ring); outline-o
|
|
|
10638
12573
|
/* Line in the accent color instead of the neutral border. */
|
|
10639
12574
|
.oc-timeline--accent { border-inline-start-color: color-mix(in oklch, var(--oc-accent-from) 55%, transparent); }
|
|
10640
12575
|
|
|
12576
|
+
/* parts/utility-hover.css */
|
|
12577
|
+
/* File: packages/ui/src/parts/utility-hover.css */
|
|
12578
|
+
/* Map: smooth timing -> lift / grow / glow / none -> steady hover area -> reduced motion */
|
|
12579
|
+
/* Hover effect of a card, chosen with one class on the card (any card: .oc-card, product, feature, link, price, project …):
|
|
12580
|
+
.oc-hover-lift moves up (--oc-hover-lift, 6px) with a soft shadow; what a product card does by default
|
|
12581
|
+
.oc-hover-grow grows a little in place (--oc-hover-scale, 1.02)
|
|
12582
|
+
.oc-hover-glow stays in place, with a glow in the accent color
|
|
12583
|
+
.oc-hover-none no movement (also turns off the default lift of a product or price card)
|
|
12584
|
+
They share one smooth curve: --oc-hover-duration (0.45s) and --oc-hover-ease. This file loads after the cards, so the class wins. */
|
|
12585
|
+
|
|
12586
|
+
:is(.oc-hover-lift, .oc-hover-grow, .oc-hover-glow, .oc-product-card) {
|
|
12587
|
+
transition:
|
|
12588
|
+
translate var(--oc-hover-duration, 0.45s) var(--oc-hover-ease, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
12589
|
+
scale var(--oc-hover-duration, 0.45s) var(--oc-hover-ease, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
12590
|
+
box-shadow var(--oc-hover-duration, 0.45s) var(--oc-hover-ease, cubic-bezier(0.22, 1, 0.36, 1)),
|
|
12591
|
+
border-color 0.35s,
|
|
12592
|
+
background-color 0.35s,
|
|
12593
|
+
--oc-glow 0.35s;
|
|
12594
|
+
}
|
|
12595
|
+
.oc-product-card:hover { translate: 0 calc(-1 * var(--oc-hover-lift, 6px)); }
|
|
12596
|
+
.oc-hover-lift:hover {
|
|
12597
|
+
translate: 0 calc(-1 * var(--oc-hover-lift, 6px));
|
|
12598
|
+
box-shadow: 0 1.25rem 2.5rem -1.25rem oklch(from var(--color-black-60) l c h / 0.35);
|
|
12599
|
+
}
|
|
12600
|
+
.oc-hover-grow:hover { translate: none; scale: var(--oc-hover-scale, 1.02); }
|
|
12601
|
+
.oc-hover-glow:hover {
|
|
12602
|
+
translate: none;
|
|
12603
|
+
box-shadow:
|
|
12604
|
+
0 0 0 1px oklch(from var(--oc-accent-from, var(--oc-primary)) l c h / 0.4),
|
|
12605
|
+
0 0.75rem 2.5rem -0.75rem oklch(from var(--oc-accent-from, var(--oc-primary)) l c h / 0.5);
|
|
12606
|
+
}
|
|
12607
|
+
.oc-hover-none:hover { translate: none; scale: none; }
|
|
12608
|
+
|
|
12609
|
+
/* While a card moves up, the strip it leaves keeps the hover, so a mouse at the bottom edge does not make it bounce. */
|
|
12610
|
+
:where(.oc-hover-lift) { position: relative; }
|
|
12611
|
+
:is(.oc-hover-lift, .oc-product-card:not([class*="oc-hover-"])):hover::before {
|
|
12612
|
+
content: "";
|
|
12613
|
+
position: absolute;
|
|
12614
|
+
inset: 0 0 calc(-1 * var(--oc-hover-lift, 6px) - 2px);
|
|
12615
|
+
z-index: -1;
|
|
12616
|
+
}
|
|
12617
|
+
|
|
12618
|
+
@media (prefers-reduced-motion: reduce) {
|
|
12619
|
+
:is(.oc-hover-lift, .oc-hover-grow, .oc-product-card):hover { translate: none; scale: none; }
|
|
12620
|
+
}
|
|
12621
|
+
|
|
10641
12622
|
/* File: packages/ui/src/code-block.css — Code-block component (line numbers + Omnia syntax theme) */
|
|
10642
12623
|
/* Map: pre.code base -> CSS variables -> token spans (.hl-tag, .hl-attr, .hl-string, etc.) */
|
|
10643
12624
|
/*
|