@birdycom/omniaui 0.12.1 → 0.12.3
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 +2 -1
- package/bin/lib/copy-js.mjs +56 -0
- package/bin/omniaui.mjs +6 -2
- package/dist/carousel.js +11 -2
- package/dist/components.css +494 -6
- package/dist/components.min.css +1 -1
- package/dist/data-table.js +11 -2
- package/dist/oc-theme.css +1 -1
- package/dist/parts/app-status-dot.css +50 -0
- package/dist/parts/app-titlebar.css +96 -0
- package/dist/parts/media-glass.css +71 -0
- package/dist/parts/media-live-frame.css +38 -0
- package/dist/parts/media-watermark.css +96 -0
- package/dist/parts/site-link-row.css +54 -0
- package/dist/parts/site-prose.css +12 -0
- package/manifest.json +48 -0
- package/package.json +2 -2
- package/snippets/glass.html +24 -0
- package/snippets/link-row.html +14 -0
- package/snippets/live-frame.html +20 -0
- package/snippets/status-dot.html +15 -0
- package/snippets/titlebar.html +29 -0
- package/snippets/watermark.html +40 -0
package/dist/data-table.js
CHANGED
|
@@ -497,8 +497,17 @@ export function initDataTables(scope = document) {
|
|
|
497
497
|
scope.querySelectorAll(".oc-data-table").forEach((root) => bindDataTable(root));
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
-
//
|
|
501
|
-
|
|
500
|
+
// Starts on its own when loaded straight from a <script type="module"> tag: as /js/oc-data-table.js (the docs site,
|
|
501
|
+
// `npx omniaui copy-js`), with ?auto in the URL, or with data-oc-auto on the tag. An import from a bundler or a
|
|
502
|
+
// wrapper does not scan the page: call the init function yourself there.
|
|
503
|
+
function autoStart() {
|
|
504
|
+
if (typeof document === "undefined") return false;
|
|
505
|
+
const url = String(import.meta.url);
|
|
506
|
+
if (url.includes("oc-data-table") || /[?&]auto(=|&|$)/.test(url)) return true;
|
|
507
|
+
const file = url.split(/[?#]/)[0];
|
|
508
|
+
return [...document.querySelectorAll("script[data-oc-auto][src]")].some((s) => s.src.split(/[?#]/)[0] === file);
|
|
509
|
+
}
|
|
510
|
+
if (autoStart()) {
|
|
502
511
|
const boot = () => initDataTables(document);
|
|
503
512
|
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot);
|
|
504
513
|
else boot();
|
package/dist/oc-theme.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* File: oc-theme.css — generated from tokens/semantic.json by @birdycom/omniacss 0.12.
|
|
1
|
+
/* File: oc-theme.css — generated from tokens/semantic.json by @birdycom/omniacss 0.12.3. 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 {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-status-dot.css
|
|
2
|
+
Map: dot (tone color + soft ring) -> tones (working pulses, waiting, done, error, idle) -> sizes -> status row (dot + text
|
|
3
|
+
that cuts off with …) -> reduced motion
|
|
4
|
+
Status dot: a small live state light for a job, agent, sync, server or person ("Working", "Waiting for input", "Done").
|
|
5
|
+
Always put a text next to it or an aria-label on it: the color alone does not tell the state. */
|
|
6
|
+
|
|
7
|
+
.oc-status-dot {
|
|
8
|
+
--oc-status-dot-color: var(--oc-muted-foreground);
|
|
9
|
+
display: inline-block;
|
|
10
|
+
flex: none;
|
|
11
|
+
inline-size: 0.5rem;
|
|
12
|
+
block-size: 0.5rem;
|
|
13
|
+
border-radius: 50%;
|
|
14
|
+
background: var(--oc-status-dot-color);
|
|
15
|
+
box-shadow: 0 0 0 2px oklch(from var(--oc-status-dot-color) l c h / 0.2);
|
|
16
|
+
vertical-align: middle;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Tones. --working pulses a ring outwards. */
|
|
20
|
+
.oc-status-dot--working {
|
|
21
|
+
--oc-status-dot-color: var(--oc-primary);
|
|
22
|
+
animation: oc-status-dot-pulse 1.8s ease-in-out infinite;
|
|
23
|
+
}
|
|
24
|
+
.oc-status-dot--waiting { --oc-status-dot-color: var(--oc-warning); }
|
|
25
|
+
.oc-status-dot--done { --oc-status-dot-color: var(--oc-success); }
|
|
26
|
+
.oc-status-dot--error { --oc-status-dot-color: var(--oc-destructive); }
|
|
27
|
+
.oc-status-dot--idle { --oc-status-dot-color: var(--oc-muted-foreground); box-shadow: none; opacity: 0.7; }
|
|
28
|
+
@keyframes oc-status-dot-pulse {
|
|
29
|
+
50% { box-shadow: 0 0 0 5px oklch(from var(--oc-status-dot-color) l c h / 0.08); }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Sizes */
|
|
33
|
+
.oc-status-dot--sm { inline-size: 0.375rem; block-size: 0.375rem; }
|
|
34
|
+
.oc-status-dot--lg { inline-size: 0.625rem; block-size: 0.625rem; }
|
|
35
|
+
|
|
36
|
+
/* Status row: dot + one line of text that cuts off with … in a narrow card. */
|
|
37
|
+
.oc-status {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: 0.5rem;
|
|
41
|
+
min-inline-size: 0;
|
|
42
|
+
margin: 0;
|
|
43
|
+
font-size: 0.8125rem;
|
|
44
|
+
}
|
|
45
|
+
.oc-status__text { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
46
|
+
.oc-status__meta { flex: none; margin-inline-start: auto; color: var(--oc-muted-foreground); font-variant-numeric: tabular-nums; }
|
|
47
|
+
|
|
48
|
+
@media (prefers-reduced-motion: reduce) {
|
|
49
|
+
.oc-status-dot--working { animation: none; }
|
|
50
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-titlebar.css
|
|
2
|
+
Map: window (column, shares the scroll timeline) -> title bar (drag area) -> title + subtitle -> actions (no drag) ->
|
|
3
|
+
icon buttons (close turns red) -> body (scrolls) -> hairline when the body scrolls (scroll-driven, no JavaScript) -> sizes
|
|
4
|
+
Title bar: the top bar of a frameless desktop window (Electron, Tauri) or a widget: you drag the window by it, the
|
|
5
|
+
buttons stay clickable. Inside .oc-titlebar-window the bar gets a hairline once .oc-titlebar-body scrolls. */
|
|
6
|
+
|
|
7
|
+
.oc-titlebar-window {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
min-block-size: 0;
|
|
11
|
+
timeline-scope: --oc-titlebar-scroll;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.oc-titlebar {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex: none;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 2px;
|
|
19
|
+
min-block-size: 46px;
|
|
20
|
+
padding: 0 8px 0 14px;
|
|
21
|
+
border-block-end: 1px solid transparent;
|
|
22
|
+
-webkit-user-select: none;
|
|
23
|
+
user-select: none;
|
|
24
|
+
/* The OS moves the window when you drag here (Electron and Tauri read this). */
|
|
25
|
+
-webkit-app-region: drag;
|
|
26
|
+
app-region: drag;
|
|
27
|
+
}
|
|
28
|
+
.oc-titlebar :is(button, a, input, select, textarea, label, [role="button"], [tabindex], .oc-titlebar__actions) {
|
|
29
|
+
-webkit-app-region: no-drag;
|
|
30
|
+
app-region: no-drag;
|
|
31
|
+
}
|
|
32
|
+
.oc-titlebar--bordered { border-block-end-color: var(--oc-glass-hairline, var(--oc-border)); }
|
|
33
|
+
|
|
34
|
+
/* Title area: grows, cuts the subtitle off with …. */
|
|
35
|
+
.oc-titlebar__main { display: flex; flex: 1; align-items: center; gap: 8px; min-inline-size: 0; }
|
|
36
|
+
.oc-titlebar__icon { flex: none; inline-size: 18px; block-size: 18px; }
|
|
37
|
+
.oc-titlebar__title { flex: none; font-size: 0.8667rem; font-weight: 650; letter-spacing: -0.01em; }
|
|
38
|
+
.oc-titlebar__subtitle {
|
|
39
|
+
min-inline-size: 0;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
color: var(--oc-muted-foreground);
|
|
42
|
+
font-size: 0.8rem;
|
|
43
|
+
text-overflow: ellipsis;
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
}
|
|
46
|
+
.oc-titlebar__subtitle::before { content: "·"; margin-inline-end: 8px; }
|
|
47
|
+
.oc-titlebar__actions { display: flex; flex: none; align-items: center; gap: 2px; }
|
|
48
|
+
|
|
49
|
+
/* Compact icon buttons: muted, a light surface on hover, the close button red. */
|
|
50
|
+
.oc-titlebar__btn {
|
|
51
|
+
display: inline-grid;
|
|
52
|
+
place-items: center;
|
|
53
|
+
inline-size: 30px;
|
|
54
|
+
block-size: 30px;
|
|
55
|
+
padding: 0;
|
|
56
|
+
border: 0;
|
|
57
|
+
border-radius: 8px;
|
|
58
|
+
background: transparent;
|
|
59
|
+
color: var(--oc-muted-foreground);
|
|
60
|
+
font: inherit;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
transition: background-color var(--oc-transition-fast), color var(--oc-transition-fast);
|
|
63
|
+
}
|
|
64
|
+
.oc-titlebar__btn :where(svg, iconify-icon) { inline-size: 16px; block-size: 16px; }
|
|
65
|
+
.oc-titlebar__btn:hover { background: var(--oc-glass-panel-strong, var(--oc-accent)); color: var(--oc-foreground); }
|
|
66
|
+
.oc-titlebar__btn:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
|
|
67
|
+
.oc-titlebar__btn--close:hover { background: var(--oc-destructive); color: var(--oc-destructive-foreground); }
|
|
68
|
+
|
|
69
|
+
/* Body: the part that scrolls under the bar. */
|
|
70
|
+
.oc-titlebar-body {
|
|
71
|
+
flex: 1;
|
|
72
|
+
min-block-size: 0;
|
|
73
|
+
overflow: auto;
|
|
74
|
+
scroll-timeline: --oc-titlebar-scroll block;
|
|
75
|
+
scrollbar-width: thin;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Hairline + light surface on the bar once the body scrolls (browsers without scroll timelines keep the plain bar). */
|
|
79
|
+
@supports (animation-timeline: scroll()) {
|
|
80
|
+
.oc-titlebar-window > .oc-titlebar:not(.oc-titlebar--bordered) {
|
|
81
|
+
animation: oc-titlebar-scrolled linear both;
|
|
82
|
+
animation-timeline: --oc-titlebar-scroll;
|
|
83
|
+
animation-range: 0 48px;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
@keyframes oc-titlebar-scrolled {
|
|
87
|
+
to { border-block-end-color: var(--oc-glass-hairline, var(--oc-border)); background: var(--oc-glass-panel, var(--oc-muted)); }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Sizes */
|
|
91
|
+
.oc-titlebar--sm { min-block-size: 36px; padding-inline-start: 10px; }
|
|
92
|
+
.oc-titlebar--sm .oc-titlebar__btn { inline-size: 26px; block-size: 26px; border-radius: 6px; }
|
|
93
|
+
|
|
94
|
+
@media (prefers-reduced-motion: reduce) {
|
|
95
|
+
.oc-titlebar-window > .oc-titlebar { animation: none; }
|
|
96
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/media-glass.css
|
|
2
|
+
Map: tokens (light-dark) -> sheet (.oc-glass: see-through, blurred, light top edge, soft shadow) -> panel (.oc-glass-panel:
|
|
3
|
+
a lighter layer inside) -> cards inside a sheet -> --flat (desktop window, the OS draws the blur) -> --strong -> reduced
|
|
4
|
+
transparency
|
|
5
|
+
Glass: see-through surfaces in the "liquid glass" style, over a photo, a gradient or the desktop (an Electron or Tauri
|
|
6
|
+
window with Windows acrylic or macOS vibrancy behind a transparent page). Re-tint with the --oc-glass-* tokens. */
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
--oc-glass-sheet: light-dark(oklch(from var(--oc-background) l c h / 0.72), oklch(from var(--oc-background) l c h / 0.62));
|
|
10
|
+
--oc-glass-panel: light-dark(oklch(100% 0 0deg / 0.5), oklch(100% 0 0deg / 0.055));
|
|
11
|
+
--oc-glass-panel-strong: light-dark(oklch(100% 0 0deg / 0.72), oklch(100% 0 0deg / 0.09));
|
|
12
|
+
--oc-glass-edge: light-dark(oklch(100% 0 0deg / 0.85), oklch(100% 0 0deg / 0.11));
|
|
13
|
+
--oc-glass-edge-hi: light-dark(oklch(100% 0 0deg / 1), oklch(100% 0 0deg / 0.16));
|
|
14
|
+
--oc-glass-hairline: light-dark(oklch(from var(--oc-foreground) l c h / 0.1), oklch(100% 0 0deg / 0.08));
|
|
15
|
+
--oc-glass-shadow: 0 24px 60px -20px light-dark(oklch(25% 0.06 255deg / 0.35), oklch(0% 0 0deg / 0.7));
|
|
16
|
+
--oc-glass-panel-shadow: 0 8px 24px -16px light-dark(oklch(25% 0.06 255deg / 0.25), oklch(0% 0 0deg / 0.6));
|
|
17
|
+
--oc-glass-blur: 28px;
|
|
18
|
+
--oc-glass-radius: 18px;
|
|
19
|
+
--oc-glass-panel-radius: 14px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Sheet: the outer glass surface (a widget, a floating panel, a dialog over a photo). */
|
|
23
|
+
.oc-glass {
|
|
24
|
+
position: relative;
|
|
25
|
+
color: var(--oc-foreground);
|
|
26
|
+
background: var(--oc-glass-sheet);
|
|
27
|
+
border: 1px solid var(--oc-glass-edge);
|
|
28
|
+
border-radius: var(--oc-glass-radius);
|
|
29
|
+
box-shadow: var(--oc-glass-shadow), inset 0 1px 0 var(--oc-glass-edge-hi);
|
|
30
|
+
backdrop-filter: blur(var(--oc-glass-blur)) saturate(1.5);
|
|
31
|
+
/* OmniaUI cards inside become glass panels too. */
|
|
32
|
+
--oc-card-background: var(--oc-glass-panel);
|
|
33
|
+
--oc-card-border: var(--oc-glass-edge);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Panel: a lighter layer inside a sheet (a group of settings, a meter, a list). */
|
|
37
|
+
.oc-glass-panel {
|
|
38
|
+
position: relative;
|
|
39
|
+
background: var(--oc-glass-panel);
|
|
40
|
+
border: 1px solid var(--oc-glass-edge);
|
|
41
|
+
border-radius: var(--oc-glass-panel-radius);
|
|
42
|
+
box-shadow: var(--oc-glass-panel-shadow), inset 0 1px 0 var(--oc-glass-edge-hi);
|
|
43
|
+
}
|
|
44
|
+
.oc-glass .oc-card {
|
|
45
|
+
border-radius: var(--oc-glass-panel-radius);
|
|
46
|
+
box-shadow: var(--oc-glass-panel-shadow), inset 0 1px 0 var(--oc-glass-edge-hi);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Strong: more opaque, for text-heavy panels or a hovered row. */
|
|
50
|
+
.oc-glass--strong,
|
|
51
|
+
.oc-glass-panel--strong { background: var(--oc-glass-panel-strong); }
|
|
52
|
+
|
|
53
|
+
/* Flat: a frameless desktop window where the OS draws the blur (acrylic, vibrancy): no own blur, no drop shadow. */
|
|
54
|
+
.oc-glass--flat {
|
|
55
|
+
border-radius: 8px;
|
|
56
|
+
box-shadow: inset 0 1px 0 var(--oc-glass-edge-hi);
|
|
57
|
+
backdrop-filter: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Hairline divider in glass (sections, header). */
|
|
61
|
+
.oc-glass-divider { block-size: 1px; margin: 0; border: 0; background: var(--oc-glass-hairline); }
|
|
62
|
+
|
|
63
|
+
/* People who ask for less transparency get solid surfaces. */
|
|
64
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
65
|
+
:root {
|
|
66
|
+
--oc-glass-sheet: var(--oc-background);
|
|
67
|
+
--oc-glass-panel: var(--oc-card);
|
|
68
|
+
--oc-glass-panel-strong: var(--oc-muted);
|
|
69
|
+
}
|
|
70
|
+
.oc-glass { backdrop-filter: none; }
|
|
71
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/media-live-frame.css
|
|
2
|
+
Map: frame (virtual size -> ratio, container) -> content at the virtual size, scaled to the frame width -> static
|
|
3
|
+
Live frame: shows a page (an <iframe>, or a <video>/<img>) at a virtual size such as 1280x720 and scales it down to
|
|
4
|
+
the width of its box, so a live site looks like a screenshot that is still alive. Pure CSS: the scale is
|
|
5
|
+
tan(atan2(100cqi, <virtual width>)), which is the frame width divided by the virtual width. On .oc-browser-frame__view
|
|
6
|
+
or on its own. Set the size with --oc-live-w / --oc-live-h (unitless px, default 1280 x 720). */
|
|
7
|
+
|
|
8
|
+
.oc-live-frame {
|
|
9
|
+
--oc-live-w: 1280;
|
|
10
|
+
--oc-live-h: 720;
|
|
11
|
+
position: relative;
|
|
12
|
+
display: block;
|
|
13
|
+
/* Always the full width of its column (also inside flex or grid centring), the height follows the ratio. */
|
|
14
|
+
inline-size: 100%;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
aspect-ratio: var(--oc-live-w) / var(--oc-live-h);
|
|
17
|
+
container-type: inline-size;
|
|
18
|
+
background: var(--oc-background);
|
|
19
|
+
}
|
|
20
|
+
/* .oc-live-frame__content: any element (a whole demo page) at the virtual size. The class inside :is() also makes this
|
|
21
|
+
rule as strong as the .oc-browser-frame__view rule, which comes earlier, so on the view slot this one wins. */
|
|
22
|
+
.oc-live-frame > :is(iframe, video, img, object, .oc-live-frame__content) {
|
|
23
|
+
position: absolute;
|
|
24
|
+
inset-block-start: 0;
|
|
25
|
+
inset-inline-start: 0;
|
|
26
|
+
display: block;
|
|
27
|
+
inline-size: calc(var(--oc-live-w) * 1px);
|
|
28
|
+
max-inline-size: none;
|
|
29
|
+
block-size: calc(var(--oc-live-h) * 1px);
|
|
30
|
+
border: 0;
|
|
31
|
+
transform-origin: 0 0;
|
|
32
|
+
scale: tan(atan2(100cqi, calc(var(--oc-live-w) * 1px)));
|
|
33
|
+
}
|
|
34
|
+
/* Right-to-left pages: scale from the other corner. */
|
|
35
|
+
.oc-live-frame:dir(rtl) > :is(iframe, video, img, object, .oc-live-frame__content) { transform-origin: 100% 0; }
|
|
36
|
+
|
|
37
|
+
/* A picture of a page, not a page to use: clicks and scrolling pass the frame by. */
|
|
38
|
+
.oc-live-frame--static > :is(iframe, video, img, object, .oc-live-frame__content) { pointer-events: none; }
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/media-watermark.css
|
|
2
|
+
Map: overlay (click-through, sized to its box) -> text -> straight -> tiled -> corner stamp -> tones -> print
|
|
3
|
+
Watermark: a light layer with large slanted text ("CONCEPT", "DRAFT", "SAMPLE") over an image, a card or a live
|
|
4
|
+
frame. Put .oc-watermark as the last child of a positioned box (the utility .relative, or any position) with the
|
|
5
|
+
text in a <span>. Clicks go through to what is underneath (pointer-events: none). The text scales with the box
|
|
6
|
+
(container query units). Tokens: --oc-watermark-color, -opacity, -angle, -size. Omnia tokens only. */
|
|
7
|
+
|
|
8
|
+
.oc-watermark {
|
|
9
|
+
--oc-watermark-color: var(--oc-foreground);
|
|
10
|
+
--oc-watermark-opacity: 0.12;
|
|
11
|
+
--oc-watermark-angle: -18deg;
|
|
12
|
+
--oc-watermark-size: clamp(1.75rem, 14cqi, 12rem);
|
|
13
|
+
position: absolute;
|
|
14
|
+
z-index: 2;
|
|
15
|
+
inset: 0;
|
|
16
|
+
display: grid;
|
|
17
|
+
place-items: center;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
container-type: size;
|
|
20
|
+
border-radius: inherit;
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
user-select: none;
|
|
23
|
+
}
|
|
24
|
+
.oc-watermark > span {
|
|
25
|
+
rotate: var(--oc-watermark-angle);
|
|
26
|
+
color: oklch(from var(--oc-watermark-color) l c h / var(--oc-watermark-opacity));
|
|
27
|
+
font-size: var(--oc-watermark-size);
|
|
28
|
+
font-weight: 800;
|
|
29
|
+
line-height: 1;
|
|
30
|
+
letter-spacing: 0.25em;
|
|
31
|
+
text-transform: uppercase;
|
|
32
|
+
white-space: nowrap;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* A logo (<img>, or an inline <svg> logo) or an icon (inline <svg> with currentColor) instead of, or next to, text.
|
|
36
|
+
An <img> keeps its own colors at the watermark opacity; --mono turns it into a flat gray stamp. An inline <svg>
|
|
37
|
+
takes the watermark color, like the text. Size: --oc-watermark-mark (default 40% of the box width). */
|
|
38
|
+
.oc-watermark > :is(img, svg) {
|
|
39
|
+
inline-size: var(--oc-watermark-mark, 40cqi);
|
|
40
|
+
max-block-size: 70cqb;
|
|
41
|
+
block-size: auto;
|
|
42
|
+
rotate: var(--oc-watermark-angle);
|
|
43
|
+
}
|
|
44
|
+
.oc-watermark > img { opacity: calc(var(--oc-watermark-opacity) * 1.5); }
|
|
45
|
+
.oc-watermark > svg { color: oklch(from var(--oc-watermark-color) l c h / var(--oc-watermark-opacity)); }
|
|
46
|
+
.oc-watermark--mono > img { filter: grayscale(1) contrast(0.6); }
|
|
47
|
+
/* Logo or icon above a word: wrap both in one <span class="oc-watermark__stack">. */
|
|
48
|
+
.oc-watermark__stack { display: grid; justify-items: center; gap: 0.3em; }
|
|
49
|
+
.oc-watermark__stack > :is(img, svg) { inline-size: 0.9em; block-size: auto; }
|
|
50
|
+
.oc-watermark__stack > img { opacity: calc(var(--oc-watermark-opacity) * 1.5); }
|
|
51
|
+
|
|
52
|
+
/* Horizontal text instead of slanted. */
|
|
53
|
+
.oc-watermark--straight { --oc-watermark-angle: 0deg; }
|
|
54
|
+
|
|
55
|
+
/* Tiled: repeat the text in several spans; they fill the box in a slanted grid (for proofs and previews). */
|
|
56
|
+
.oc-watermark--tile {
|
|
57
|
+
--oc-watermark-size: clamp(0.9rem, 4.5cqi, 2.25rem);
|
|
58
|
+
/* Columns in rem: container units here would measure the parent of the box, not the box. */
|
|
59
|
+
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
|
|
60
|
+
align-content: space-evenly;
|
|
61
|
+
inset: -25%;
|
|
62
|
+
rotate: var(--oc-watermark-angle);
|
|
63
|
+
}
|
|
64
|
+
.oc-watermark--tile > :is(span, img, svg) { rotate: none; justify-self: center; }
|
|
65
|
+
.oc-watermark--tile > span { letter-spacing: 0.15em; }
|
|
66
|
+
.oc-watermark--tile > :is(img, svg) { inline-size: 2.5rem; }
|
|
67
|
+
/* Logos and icons are narrower than words: narrower columns, so the whole box fills up. */
|
|
68
|
+
.oc-watermark--tile:has(> :is(img, svg)) { grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr)); }
|
|
69
|
+
|
|
70
|
+
/* A small stamp in a corner instead of across the middle: --corner (bottom right) or --corner-top (top right).
|
|
71
|
+
Two classes, so a tone (--primary …) changes its color but not its readable opacity. */
|
|
72
|
+
.oc-watermark:is(.oc-watermark--corner, .oc-watermark--corner-top) {
|
|
73
|
+
--oc-watermark-angle: 0deg;
|
|
74
|
+
--oc-watermark-size: 0.75rem;
|
|
75
|
+
--oc-watermark-opacity: 0.75;
|
|
76
|
+
place-items: end;
|
|
77
|
+
padding: 0.75rem;
|
|
78
|
+
}
|
|
79
|
+
.oc-watermark--corner-top { place-items: start end; }
|
|
80
|
+
.oc-watermark:is(.oc-watermark--corner, .oc-watermark--corner-top) > span {
|
|
81
|
+
padding: 0.25rem 0.6rem;
|
|
82
|
+
border: 1px solid oklch(from var(--oc-watermark-color) l c h / 0.35);
|
|
83
|
+
border-radius: var(--oc-radius, 0.375rem);
|
|
84
|
+
background: oklch(from var(--oc-background) l c h / 0.7);
|
|
85
|
+
letter-spacing: 0.12em;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Tones: light text for dark images, the brand color, or red for "not for use". */
|
|
89
|
+
.oc-watermark--light { --oc-watermark-color: var(--color-white-100); --oc-watermark-opacity: 0.22; }
|
|
90
|
+
.oc-watermark--primary { --oc-watermark-color: var(--oc-primary); --oc-watermark-opacity: 0.16; }
|
|
91
|
+
.oc-watermark--destructive { --oc-watermark-color: var(--oc-destructive); --oc-watermark-opacity: 0.2; }
|
|
92
|
+
|
|
93
|
+
/* Printed proofs keep the watermark, a little stronger so it survives the printer. */
|
|
94
|
+
@media print {
|
|
95
|
+
.oc-watermark { --oc-watermark-opacity: 0.2; print-color-adjust: exact; }
|
|
96
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/site-link-row.css
|
|
2
|
+
Map: row (wraps) -> link -> external icon (http links) -> GitHub mark (github.com links) -> tones -> sizes
|
|
3
|
+
Link row: a row of links under a feature or product card ("Marketplace", "Source", "Docs"). Links to another site get
|
|
4
|
+
an "opens elsewhere" arrow by themselves, links to github.com the GitHub mark instead; links on your own site get
|
|
5
|
+
no icon. The icons are CSS masks in the text color, so they follow the tone and light/dark mode. */
|
|
6
|
+
|
|
7
|
+
.oc-link-row {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-wrap: wrap;
|
|
10
|
+
gap: 0.4rem 1rem;
|
|
11
|
+
margin: 0.9rem 0 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
list-style: none;
|
|
14
|
+
font-size: 0.9rem;
|
|
15
|
+
}
|
|
16
|
+
.oc-link-row a {
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: 0.35rem;
|
|
20
|
+
border-radius: 4px;
|
|
21
|
+
color: var(--oc-primary);
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
}
|
|
25
|
+
.oc-link-row a:hover { text-decoration: underline; text-underline-offset: 0.2em; }
|
|
26
|
+
.oc-link-row a:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 3px; }
|
|
27
|
+
|
|
28
|
+
/* Icons: one mask per kind, filled with the text color. */
|
|
29
|
+
.oc-link-row a:is([href^="http://"], [href^="https://"])::after,
|
|
30
|
+
.oc-link-row a[href*="github.com"]::before {
|
|
31
|
+
content: "";
|
|
32
|
+
flex: none;
|
|
33
|
+
inline-size: 0.9em;
|
|
34
|
+
aspect-ratio: 1;
|
|
35
|
+
background: currentColor;
|
|
36
|
+
mask: var(--oc-link-row-icon) center / contain no-repeat;
|
|
37
|
+
}
|
|
38
|
+
.oc-link-row a:is([href^="http://"], [href^="https://"])::after {
|
|
39
|
+
--oc-link-row-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7M9 7h8v8'/%3E%3C/svg%3E");
|
|
40
|
+
}
|
|
41
|
+
.oc-link-row a[href*="github.com"]::before {
|
|
42
|
+
--oc-link-row-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 .5a11.5 11.5 0 0 0-3.64 22.41c.58.1.79-.25.79-.56v-2c-3.2.7-3.88-1.37-3.88-1.37-.52-1.33-1.28-1.69-1.28-1.69-1.05-.71.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.77 2.7 1.26 3.36.96.1-.75.4-1.26.73-1.55-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.28 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.17 1.18a11 11 0 0 1 5.77 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.11 3.05.74.81 1.19 1.83 1.19 3.09 0 4.42-2.7 5.39-5.26 5.68.41.36.78 1.06.78 2.14v3.17c0 .31.21.67.8.56A11.5 11.5 0 0 0 12 .5Z'/%3E%3C/svg%3E");
|
|
43
|
+
}
|
|
44
|
+
/* A GitHub link shows the GitHub mark instead of the arrow. */
|
|
45
|
+
.oc-link-row a[href*="github.com"]::after { content: none; }
|
|
46
|
+
|
|
47
|
+
/* Tones: the element's accent (with an .oc-accent--* class), or muted text that brightens on hover. */
|
|
48
|
+
.oc-link-row--accent a { color: light-dark(var(--oc-accent-from, var(--oc-primary)), oklch(from var(--oc-accent-from, var(--oc-primary)) max(l, 0.72) c h)); }
|
|
49
|
+
.oc-link-row--muted a { color: var(--oc-muted-foreground); font-weight: 500; }
|
|
50
|
+
.oc-link-row--muted a:hover { color: var(--oc-foreground); }
|
|
51
|
+
|
|
52
|
+
/* Sizes */
|
|
53
|
+
.oc-link-row--sm { font-size: 0.8rem; gap: 0.3rem 0.8rem; }
|
|
54
|
+
.oc-link-row--lg { font-size: 1rem; }
|
|
@@ -119,6 +119,18 @@
|
|
|
119
119
|
.oc-prose--lg { font-size: 1.125rem; line-height: 1.75; }
|
|
120
120
|
.oc-prose--full { max-inline-size: none; }
|
|
121
121
|
|
|
122
|
+
/* Compact: help text and about pages in a widget, side panel or popover. Smaller text, tighter rhythm and headings,
|
|
123
|
+
full width of the panel. */
|
|
124
|
+
.oc-prose--compact { max-inline-size: none; font-size: 0.8125rem; line-height: 1.5; }
|
|
125
|
+
.oc-prose--compact :where(* + *) { margin-block-start: 0.7em; }
|
|
126
|
+
.oc-prose--compact :where(h1) { font-size: 1.45em; }
|
|
127
|
+
.oc-prose--compact :where(h2) { margin-block-start: 1.4em; font-size: 1.15em; }
|
|
128
|
+
.oc-prose--compact :where(h3) { margin-block-start: 1.2em; font-size: 1.05em; }
|
|
129
|
+
.oc-prose--compact :where(h4) { margin-block-start: 1em; }
|
|
130
|
+
.oc-prose--compact :where(h1, h2, h3, h4) + :where(*) { margin-block-start: 0.35em; }
|
|
131
|
+
.oc-prose--compact :where(.oc-prose-lead) { font-size: 1.08em; }
|
|
132
|
+
.oc-prose--compact > :where(:first-child) { margin-block-start: 0; }
|
|
133
|
+
|
|
122
134
|
@media (prefers-reduced-motion: reduce) {
|
|
123
135
|
.oc-prose :where(a) { transition: none; }
|
|
124
136
|
}
|
package/manifest.json
CHANGED
|
@@ -1111,6 +1111,54 @@
|
|
|
1111
1111
|
"requires": [
|
|
1112
1112
|
"@birdycom/omniaui"
|
|
1113
1113
|
]
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
"name": "watermark",
|
|
1117
|
+
"file": "snippets/watermark.html",
|
|
1118
|
+
"description": "Light slanted text such as CONCEPT or DRAFT over an image, a card or a live frame",
|
|
1119
|
+
"requires": [
|
|
1120
|
+
"@birdycom/omniaui"
|
|
1121
|
+
]
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
"name": "live-frame",
|
|
1125
|
+
"file": "snippets/live-frame.html",
|
|
1126
|
+
"description": "A live page at a virtual size such as 1280x720, scaled down to the frame width without JavaScript",
|
|
1127
|
+
"requires": [
|
|
1128
|
+
"@birdycom/omniaui"
|
|
1129
|
+
]
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
"name": "link-row",
|
|
1133
|
+
"file": "snippets/link-row.html",
|
|
1134
|
+
"description": "A row of links under a feature card, with an external icon and the GitHub mark by themselves",
|
|
1135
|
+
"requires": [
|
|
1136
|
+
"@birdycom/omniaui"
|
|
1137
|
+
]
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
"name": "glass",
|
|
1141
|
+
"file": "snippets/glass.html",
|
|
1142
|
+
"description": "See-through glass sheet and panels with a light top edge, for widgets and panels over a photo, a gradient or the desktop",
|
|
1143
|
+
"requires": [
|
|
1144
|
+
"@birdycom/omniaui"
|
|
1145
|
+
]
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
"name": "status-dot",
|
|
1149
|
+
"file": "snippets/status-dot.html",
|
|
1150
|
+
"description": "A small live state light for a job, agent, sync or server, with the state in text next to it",
|
|
1151
|
+
"requires": [
|
|
1152
|
+
"@birdycom/omniaui"
|
|
1153
|
+
]
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
"name": "titlebar",
|
|
1157
|
+
"file": "snippets/titlebar.html",
|
|
1158
|
+
"description": "Draggable title bar for a frameless desktop window or widget, with a hairline once the body scrolls",
|
|
1159
|
+
"requires": [
|
|
1160
|
+
"@birdycom/omniaui"
|
|
1161
|
+
]
|
|
1114
1162
|
}
|
|
1115
1163
|
]
|
|
1116
1164
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@birdycom/omniaui",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"description": "OmniaUI components: CSS-first UI built on native HTML (dialog, popover, details) with light-dark() theming — no JavaScript required. Includes the omniaui CLI (add/list).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"omniaui": "./bin/omniaui.mjs"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@birdycom/omniacss": "^0.12.
|
|
60
|
+
"@birdycom/omniacss": "^0.12.3"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
63
63
|
"@birdycom/omniacss": {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!-- OmniaUI component: glass - See-through glass sheet and panels with a light top edge, for widgets and panels over a photo, a gradient or the desktop. Needs @birdycom/omniaui CSS.
|
|
2
|
+
File: packages/ui/snippets/glass.html | Map: div.oc-glass (sheet) -> content -> div.oc-glass-panel (layer) / .oc-card (becomes a glass panel)
|
|
3
|
+
Class API (modifiers are extra classes on the same element):
|
|
4
|
+
.oc-glass: the outer sheet (see-through, blurred background, light top edge, soft shadow); .oc-card inside turns into a glass panel
|
|
5
|
+
.oc-glass-panel: a lighter layer inside a sheet; .oc-glass-divider: hairline
|
|
6
|
+
Modifiers: --strong (more opaque), --flat (desktop window where the OS draws the blur: no own blur, no shadow)
|
|
7
|
+
Tokens: --oc-glass-sheet, -panel, -panel-strong, -edge, -edge-hi, -hairline, -shadow, -panel-shadow, -blur, -radius, -panel-radius
|
|
8
|
+
Solid surfaces with prefers-reduced-transparency: reduce.
|
|
9
|
+
Needs something behind it to show through: a photo, a gradient, or a transparent window (Electron, Tauri).
|
|
10
|
+
-->
|
|
11
|
+
<div class="oc-glass p-1 grid gap-0.7" style="max-inline-size: 24rem">
|
|
12
|
+
<div class="flex align-items-center justify-between gap-0.5">
|
|
13
|
+
<strong>Usage today</strong>
|
|
14
|
+
<span class="oc-badge oc-badge--outline">Pro plan</span>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="oc-glass-panel p-0.7 grid gap-0.5">
|
|
17
|
+
<div class="flex justify-between font-0.8"><span>Session</span><span class="font-num-tabular">34%</span></div>
|
|
18
|
+
<div class="oc-progress oc-progress--sm oc-progress--glow" role="progressbar" aria-valuenow="34" aria-valuemin="0" aria-valuemax="100" aria-label="Session usage">
|
|
19
|
+
<span class="oc-progress-bar" style="width: 34%"></span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<hr class="oc-glass-divider">
|
|
23
|
+
<p class="m-0 font-0.8" style="color: var(--oc-muted-foreground)">Resets at 18:00.</p>
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- OmniaUI component: link-row - A row of links under a feature card, with an external icon and the GitHub mark by themselves. Needs @birdycom/omniaui CSS.
|
|
2
|
+
File: packages/ui/snippets/link-row.html | Map: ul.oc-link-row -> li > a (own site: no icon; other site: arrow; github.com: mark)
|
|
3
|
+
Class API (modifiers are extra classes on the same element):
|
|
4
|
+
.oc-link-row: wrapping row of links, primary color
|
|
5
|
+
Icons: by the href (http(s) links: arrow; github.com: GitHub mark; relative links: none)
|
|
6
|
+
Modifiers: --accent (element accent, with .oc-accent--*), --muted, --sm, --lg
|
|
7
|
+
Slots: the list items
|
|
8
|
+
-->
|
|
9
|
+
<ul class="oc-link-row">
|
|
10
|
+
<li><a href="/docs/editor">How it works</a></li>
|
|
11
|
+
<li><a href="https://marketplace.visualstudio.com/items?itemName=Birdycom.omniacss-intellisense" rel="noopener">VS Code Marketplace</a></li>
|
|
12
|
+
<li><a href="https://open-vsx.org/extension/Birdycom/omniacss-intellisense" rel="noopener">Open VSX</a></li>
|
|
13
|
+
<li><a href="https://github.com/Birdycom/OmniaCSS" rel="noopener">Source</a></li>
|
|
14
|
+
</ul>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- OmniaUI component: live-frame - A live page at a virtual size such as 1280x720, scaled down to the frame width without JavaScript. Needs @birdycom/omniaui CSS.
|
|
2
|
+
File: packages/ui/snippets/live-frame.html | Map: browser frame with a live view -> a live frame on its own (static, other size)
|
|
3
|
+
Class API (modifiers are extra classes on the same element):
|
|
4
|
+
.oc-live-frame: box with the ratio of the virtual size; its iframe / video / img / .oc-live-frame__content is laid out
|
|
5
|
+
at that size and scaled to the box width (pure CSS: tan(atan2(100cqi, width)))
|
|
6
|
+
Modifiers: --static (clicks and scrolling pass through: a picture of a page)
|
|
7
|
+
Tokens: --oc-live-w, --oc-live-h (unitless px, default 1280 x 720)
|
|
8
|
+
Also on .oc-browser-frame__view, the view slot of the browser frame.
|
|
9
|
+
Slots: the iframe (or other content). Give the iframe a title; scrolling="no" hides its scrollbar.
|
|
10
|
+
-->
|
|
11
|
+
<div class="oc-browser-frame">
|
|
12
|
+
<div class="oc-browser-frame__bar"><i></i><i></i><i></i><span class="oc-browser-frame__address">omniaui.eu</span></div>
|
|
13
|
+
<div class="oc-browser-frame__view oc-live-frame">
|
|
14
|
+
<iframe src="https://omniaui.eu/" title="OmniaUI home page" loading="lazy" scrolling="no"></iframe>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="oc-live-frame oc-live-frame--static" style="--oc-live-w: 1680; --oc-live-h: 1400">
|
|
19
|
+
<iframe src="https://omniacss.eu/" title="OmniaCSS home page" loading="lazy" scrolling="no" tabindex="-1"></iframe>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!-- OmniaUI component: status-dot - A small live state light for a job, agent, sync or server, with the state in text next to it. Needs @birdycom/omniaui CSS.
|
|
2
|
+
File: packages/ui/snippets/status-dot.html | Map: p.oc-status -> span.oc-status-dot--{tone} + span.oc-status__text (+ span.oc-status__meta)
|
|
3
|
+
Class API (modifiers are extra classes on the same element):
|
|
4
|
+
.oc-status-dot: 8 px dot with a soft ring in the tone color
|
|
5
|
+
Tones: --working (primary, pulses), --waiting (warning), --done (success), --error (destructive), --idle (muted); none = muted
|
|
6
|
+
Sizes: --sm, --lg
|
|
7
|
+
.oc-status: dot + one line of text that cuts off with …; .oc-status__text, .oc-status__meta (right, tabular numbers)
|
|
8
|
+
Token: --oc-status-dot-color (any color)
|
|
9
|
+
The dot is decoration (aria-hidden): the text says the state. A dot without text needs role="img" and an aria-label.
|
|
10
|
+
-->
|
|
11
|
+
<p class="oc-status"><span class="oc-status-dot oc-status-dot--working" aria-hidden="true"></span><span class="oc-status__text">Working · Reading src/styles/theme.css</span><span class="oc-status__meta">2:14</span></p>
|
|
12
|
+
<p class="oc-status"><span class="oc-status-dot oc-status-dot--waiting" aria-hidden="true"></span><span class="oc-status__text">Waiting for your answer</span></p>
|
|
13
|
+
<p class="oc-status"><span class="oc-status-dot oc-status-dot--done" aria-hidden="true"></span><span class="oc-status__text">Done · 3 files changed</span><span class="oc-status__meta">12 min</span></p>
|
|
14
|
+
<p class="oc-status"><span class="oc-status-dot oc-status-dot--error" aria-hidden="true"></span><span class="oc-status__text">Stopped: the build failed</span></p>
|
|
15
|
+
<p class="oc-status"><span class="oc-status-dot oc-status-dot--idle" aria-hidden="true"></span><span class="oc-status__text">Idle</span></p>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!-- OmniaUI component: titlebar - Draggable title bar for a frameless desktop window or widget, with a hairline once the body scrolls. Needs @birdycom/omniaui CSS.
|
|
2
|
+
File: packages/ui/snippets/titlebar.html | Map: div.oc-titlebar-window -> header.oc-titlebar (main: icon, title, subtitle | actions: buttons) -> div.oc-titlebar-body
|
|
3
|
+
Class API (modifiers are extra classes on the same element):
|
|
4
|
+
.oc-titlebar-window: column that shares the scroll timeline between bar and body
|
|
5
|
+
.oc-titlebar: the bar; app-region: drag (Electron, Tauri move the window by it), buttons and fields stay clickable
|
|
6
|
+
.oc-titlebar__main / __icon / __title / __subtitle (· before it, cuts off with …) / __actions
|
|
7
|
+
.oc-titlebar__btn: compact icon button; --close turns red on hover
|
|
8
|
+
.oc-titlebar-body: the part that scrolls; the bar gets a hairline once it scrolls (scroll-driven, no JavaScript)
|
|
9
|
+
Modifiers: --bordered (hairline always), --sm
|
|
10
|
+
Icon buttons need an aria-label. Inside a .oc-glass sheet the bar uses the glass hairline and surfaces.
|
|
11
|
+
-->
|
|
12
|
+
<div class="oc-titlebar-window" style="block-size: 16rem">
|
|
13
|
+
<header class="oc-titlebar">
|
|
14
|
+
<div class="oc-titlebar__main">
|
|
15
|
+
<span class="oc-titlebar__title">TokenTally</span>
|
|
16
|
+
<span class="oc-titlebar__subtitle">Usage</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="oc-titlebar__actions">
|
|
19
|
+
<button type="button" class="oc-titlebar__btn" aria-label="Refresh"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-2.64-6.36L21 8"/><path d="M21 3v5h-5"/></svg></button>
|
|
20
|
+
<button type="button" class="oc-titlebar__btn" aria-label="Settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z"/></svg></button>
|
|
21
|
+
<button type="button" class="oc-titlebar__btn oc-titlebar__btn--close" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button>
|
|
22
|
+
</div>
|
|
23
|
+
</header>
|
|
24
|
+
<div class="oc-titlebar-body p-1">
|
|
25
|
+
<p>Scroll this area: the bar gets a hairline.</p>
|
|
26
|
+
<p>Drag the bar to move a frameless Electron or Tauri window; the buttons stay clickable.</p>
|
|
27
|
+
<p>Line 3</p><p>Line 4</p><p>Line 5</p><p>Line 6</p><p>Line 7</p><p>Line 8</p>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|