@birdycom/omniaui 0.12.4 → 0.12.6
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 +10 -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 +1496 -193
- package/dist/components.min.css +1 -1
- package/dist/data-table.js +135 -98
- 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 +1 -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-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-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-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-header-variants.css +70 -0
- package/dist/parts/site-prose.css +1 -1
- package/dist/showcase.css +40 -9
- package/manifest.json +121 -9
- package/package.json +5 -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/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/form-density.html +25 -0
- package/snippets/form-section.html +47 -0
- package/snippets/gallery.html +25 -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/price.html +25 -0
- package/snippets/prose.html +3 -3
- package/snippets/quantity.html +12 -0
- package/snippets/rating.html +26 -0
- package/snippets/ribbon.html +48 -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
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/* 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 */
|
|
2
|
+
/* The rich relations list on top of app-data-table.css: tabs that filter a column, filter selects with an icon,
|
|
3
|
+
rich cells and the label/value lines of the card view. Behaviour: data-oc-col-filter in src/data-table.js. */
|
|
4
|
+
|
|
5
|
+
/* Every control is 2rem high: search, filter selects, trash pills, icon buttons, rows per page and the pager. */
|
|
6
|
+
.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; }
|
|
7
|
+
.oc-data-table .oc-data-table-page { min-block-size: 2rem; }
|
|
8
|
+
.oc-data-table :is(.oc-data-table-toolbar, .oc-data-table-filters, thead) .oc-btn--icon { --oc-btn-square: 2rem; }
|
|
9
|
+
.oc-data-table-toolbar .oc-filter-pill {
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
block-size: 2rem;
|
|
13
|
+
padding-inline: 0.625rem;
|
|
14
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Header: the sort mark only on the sorted column (and on hover); the actions header holds the column button. */
|
|
18
|
+
/* The sort mark always keeps its 1rem, so sorting another column never moves the header: empty while unsorted,
|
|
19
|
+
faint chevrons on hover, the accent sort icon on the sorted column. */
|
|
20
|
+
:is(.oc-data-table, .oc-block-datatable) th:has(.oc-table-sort) { cursor: pointer; }
|
|
21
|
+
:is(.oc-data-table, .oc-block-datatable) th .oc-table-sort::after { content: ""; flex-shrink: 0; inline-size: 1rem; block-size: 1rem; opacity: 1; }
|
|
22
|
+
:is(.oc-data-table, .oc-block-datatable) th:not([aria-sort]):hover .oc-table-sort::after {
|
|
23
|
+
background: var(--oc-muted-foreground);
|
|
24
|
+
opacity: 0.6;
|
|
25
|
+
-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;
|
|
26
|
+
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;
|
|
27
|
+
}
|
|
28
|
+
/* Sorted column: a bold label and a sort icon in the accent color. aria-sort on a header in the HTML
|
|
29
|
+
is the column sorted when the page opens. */
|
|
30
|
+
:is(.oc-data-table, .oc-block-datatable) th[aria-sort] .oc-table-sort { color: var(--oc-foreground); }
|
|
31
|
+
:is(.oc-data-table, .oc-block-datatable) th[aria-sort] .oc-table-sort::after {
|
|
32
|
+
content: "";
|
|
33
|
+
inline-size: 1rem;
|
|
34
|
+
block-size: 1rem;
|
|
35
|
+
opacity: 1;
|
|
36
|
+
background: var(--oc-primary);
|
|
37
|
+
-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;
|
|
38
|
+
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;
|
|
39
|
+
}
|
|
40
|
+
:is(.oc-data-table, .oc-block-datatable) th[aria-sort="descending"] .oc-table-sort::after {
|
|
41
|
+
-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");
|
|
42
|
+
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");
|
|
43
|
+
}
|
|
44
|
+
.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); }
|
|
45
|
+
.oc-data-table td[data-oc-col="actions"] { text-align: end; }
|
|
46
|
+
/* Row actions per status: data-oc-when="active" (edit, view) only on live rows, data-oc-when="deleted" (restore,
|
|
47
|
+
delete permanently) only on rows in the trash. */
|
|
48
|
+
.oc-data-table tr[data-oc-status="deleted"] [data-oc-when="active"],
|
|
49
|
+
.oc-data-table tr:not([data-oc-status="deleted"]) [data-oc-when="deleted"] { display: none; }
|
|
50
|
+
/* The column chooser in the actions header: a large plain icon (1.75rem), no button frame. */
|
|
51
|
+
.oc-data-table-columns-btn { font-size: 1.75rem; }
|
|
52
|
+
.oc-data-table-columns-btn > :is(svg, iconify-icon) { font-size: 1em; inline-size: 1em; block-size: 1em; }
|
|
53
|
+
|
|
54
|
+
/* Tabs above the toolbar (All / Customers / Companies …): icon, label and a count; they filter one column. */
|
|
55
|
+
.oc-data-table-tabs { display: flex; inline-size: 100%; overflow-x: auto; scrollbar-width: none; }
|
|
56
|
+
.oc-data-table-tabs .oc-tab-trigger { flex-shrink: 0; }
|
|
57
|
+
.oc-data-table-tabs .oc-tab-trigger { display: inline-flex; align-items: center; gap: 0.4rem; }
|
|
58
|
+
.oc-data-table-tabs .oc-tab-trigger :is(svg, iconify-icon) { font-size: 1.1em; inline-size: 1.1em; block-size: 1.1em; }
|
|
59
|
+
.oc-data-table-tabs .oc-tab-trigger[aria-selected="true"] { color: var(--oc-primary); border-bottom-color: var(--oc-primary); }
|
|
60
|
+
.oc-data-table-tab-count {
|
|
61
|
+
min-inline-size: 1.25rem;
|
|
62
|
+
padding: 0 0.375rem;
|
|
63
|
+
font-size: 0.6875rem;
|
|
64
|
+
line-height: 1.125rem;
|
|
65
|
+
text-align: center;
|
|
66
|
+
color: var(--oc-muted-foreground);
|
|
67
|
+
background: var(--oc-muted);
|
|
68
|
+
border-radius: 999px;
|
|
69
|
+
}
|
|
70
|
+
.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); }
|
|
71
|
+
/* A count in the color of its type (the same tone as the type badge in the table). */
|
|
72
|
+
.oc-data-table-tab-count:is(.oc-data-table-tab-count--success, .oc-data-table-tab-count--info, .oc-data-table-tab-count--warning) {
|
|
73
|
+
color: var(--oc-tab-tone);
|
|
74
|
+
background: oklch(from var(--oc-tab-tone) l c h / 0.12);
|
|
75
|
+
box-shadow: inset 0 0 0 1px oklch(from var(--oc-tab-tone) l c h / 0.35);
|
|
76
|
+
}
|
|
77
|
+
.oc-data-table-tab-count--success { --oc-tab-tone: var(--oc-success); }
|
|
78
|
+
.oc-data-table-tab-count--info { --oc-tab-tone: var(--oc-info, light-dark(var(--color-deepskyblue-70), var(--color-skyblue-50))); }
|
|
79
|
+
.oc-data-table-tab-count--warning { --oc-tab-tone: var(--oc-warning); }
|
|
80
|
+
|
|
81
|
+
/* Filter row: selects with a leading icon (status, type …) and the column button on the right. */
|
|
82
|
+
.oc-data-table-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
|
|
83
|
+
.oc-data-table-filters[hidden] { display: none; }
|
|
84
|
+
.oc-data-table-filters > :last-child:is(.oc-btn) { margin-inline-start: auto; }
|
|
85
|
+
.oc-data-table-filter { position: relative; display: inline-flex; align-items: center; }
|
|
86
|
+
.oc-data-table-filter > :is(svg, iconify-icon) {
|
|
87
|
+
position: absolute;
|
|
88
|
+
inset-inline-start: 0.625rem;
|
|
89
|
+
font-size: 1rem;
|
|
90
|
+
inline-size: 1rem;
|
|
91
|
+
block-size: 1rem;
|
|
92
|
+
color: var(--oc-muted-foreground);
|
|
93
|
+
pointer-events: none;
|
|
94
|
+
}
|
|
95
|
+
.oc-data-table-filter > .oc-select { inline-size: auto; min-inline-size: 11rem; padding-inline-start: 2.125rem; }
|
|
96
|
+
|
|
97
|
+
/* Cells. Name: avatar, name with a badge, and a muted second line (VAT number, e-mail …). */
|
|
98
|
+
.oc-data-table-name { display: flex; align-items: center; gap: 0.625rem; min-inline-size: 0; }
|
|
99
|
+
.oc-data-table-name .oc-avatar { inline-size: 2.25rem; block-size: 2.25rem; font-size: 0.75rem; }
|
|
100
|
+
.oc-data-table-name-text { display: grid; min-inline-size: 0; }
|
|
101
|
+
.oc-data-table-name-line { display: flex; align-items: center; gap: 0.375rem; font-weight: 600; }
|
|
102
|
+
.oc-data-table-name-sub { font-size: 0.75rem; color: var(--oc-muted-foreground); }
|
|
103
|
+
/* One line with an ellipsis; put the full text in title="". */
|
|
104
|
+
.oc-data-table-truncate { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-inline-size: var(--oc-data-table-truncate, 18rem); }
|
|
105
|
+
/* A count that opens the related list (projects, invoices …); muted while it is 0. */
|
|
106
|
+
.oc-data-table-count { font-variant-numeric: tabular-nums; }
|
|
107
|
+
.oc-data-table-count[data-count="0"] { color: var(--oc-muted-foreground); opacity: 0.75; }
|
|
108
|
+
/* The sort is visible in the header (icon, bold label); the summary line is for screen readers only. */
|
|
109
|
+
.oc-data-table-sort-summary {
|
|
110
|
+
position: absolute;
|
|
111
|
+
inline-size: 1px;
|
|
112
|
+
block-size: 1px;
|
|
113
|
+
margin: 0;
|
|
114
|
+
overflow: hidden;
|
|
115
|
+
clip-path: inset(50%);
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
}
|
|
118
|
+
.oc-data-table-file { display: none; }
|
|
119
|
+
|
|
120
|
+
/* The other visible columns as label / value lines, and the row actions at the bottom. */
|
|
121
|
+
.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; }
|
|
122
|
+
.oc-data-table-card-fields dt { color: var(--oc-muted-foreground); }
|
|
123
|
+
.oc-data-table-card-fields dd { margin: 0; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
124
|
+
.oc-data-table-card-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.375rem; margin-block-start: auto; }
|
|
125
|
+
|
|
126
|
+
/* Code cells (article numbers, SKUs): monospace and a step smaller. */
|
|
127
|
+
.oc-data-table-code { font-family: var(--oc-font-mono); font-size: 0.75rem; }
|
|
128
|
+
|
|
129
|
+
/* Version 2, ".oc-data-table--bordered": a tinted header row, a line between the columns and the
|
|
130
|
+
sort chevrons always visible; the filter select has its icon in a zone of its own. The rest is the same table. */
|
|
131
|
+
.oc-data-table.oc-data-table--bordered .oc-table :is(th, td) { border-inline-end: 1px solid var(--oc-border); }
|
|
132
|
+
.oc-data-table.oc-data-table--bordered .oc-table :is(th, td):last-child { border-inline-end: 0; }
|
|
133
|
+
.oc-data-table.oc-data-table--bordered .oc-table thead th {
|
|
134
|
+
font-weight: 500;
|
|
135
|
+
background: color-mix(in oklch, var(--oc-card), var(--oc-foreground) 4%);
|
|
136
|
+
}
|
|
137
|
+
.oc-data-table.oc-data-table--bordered .oc-table thead th[data-oc-col="actions"] {
|
|
138
|
+
background: color-mix(in oklch, var(--oc-card), var(--oc-foreground) 4%);
|
|
139
|
+
}
|
|
140
|
+
.oc-data-table--bordered td[data-oc-col="actions"] { text-align: center; }
|
|
141
|
+
.oc-data-table--bordered th[data-oc-col="actions"] .oc-data-table-th { justify-content: center; }
|
|
142
|
+
.oc-data-table--bordered th:not([aria-sort]) .oc-table-sort::after,
|
|
143
|
+
.oc-data-table--bordered th:not([aria-sort]):hover .oc-table-sort::after {
|
|
144
|
+
content: "";
|
|
145
|
+
inline-size: 0.875rem;
|
|
146
|
+
block-size: 0.875rem;
|
|
147
|
+
opacity: 0.5;
|
|
148
|
+
background: currentColor;
|
|
149
|
+
-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;
|
|
150
|
+
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;
|
|
151
|
+
}
|
|
152
|
+
.oc-data-table--bordered .oc-data-table-filter::after {
|
|
153
|
+
content: "";
|
|
154
|
+
position: absolute;
|
|
155
|
+
inset-block: 0;
|
|
156
|
+
inset-inline-start: 2rem;
|
|
157
|
+
inline-size: 1px;
|
|
158
|
+
background: var(--oc-border);
|
|
159
|
+
pointer-events: none;
|
|
160
|
+
}
|
|
161
|
+
.oc-data-table--bordered .oc-data-table-filter > .oc-select { padding-inline-start: 2.75rem; }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
/* File: packages/ui/src/parts/app-data-table.css | Map: shell -> toolbar -> frame and scroll -> header
|
|
2
|
-
/* List table
|
|
1
|
+
/* 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 */
|
|
2
|
+
/* List table for business apps. The frame, toolbar and footer are styling. Tabs, filter row, rich cells and
|
|
3
|
+
card fields: app-data-table-rich.css; footer and pager: app-data-table-footer.css.
|
|
3
4
|
Behaviour (search, sort, pages, resize, drag, trash, bulk) lives in src/data-table.js.
|
|
4
5
|
Set --oc-data-table-max on the shell when the body should scroll and the header should stick. */
|
|
5
6
|
|
|
@@ -12,13 +13,16 @@
|
|
|
12
13
|
background: var(--oc-card);
|
|
13
14
|
border: 1px solid var(--oc-border);
|
|
14
15
|
border-radius: var(--oc-radius);
|
|
16
|
+
/* Full width, and in a grid or flex parent the table scrolls inside the frame instead of stretching the card. */
|
|
17
|
+
inline-size: 100%;
|
|
18
|
+
min-inline-size: 0;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
/* Toolbar: title + gear, search, trash pills, table/cards toggle, page actions. */
|
|
21
|
+
/* Toolbar: title + gear/filter button, search (takes the free width), trash pills, table/cards toggle, page actions. */
|
|
18
22
|
.oc-data-table-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
|
|
19
23
|
.oc-data-table-heading { display: flex; align-items: center; gap: 0.5rem; min-inline-size: 0; }
|
|
20
24
|
.oc-data-table-title { font-size: 0.875rem; font-weight: 600; }
|
|
21
|
-
.oc-data-table-search { flex: 1 1
|
|
25
|
+
.oc-data-table-search { flex: 1 1 10rem; min-inline-size: 9rem; }
|
|
22
26
|
.oc-data-table-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-inline-start: auto; }
|
|
23
27
|
.oc-data-table-view { display: inline-flex; gap: 0.25rem; }
|
|
24
28
|
.oc-data-table-view [aria-pressed="true"] {
|
|
@@ -26,55 +30,52 @@
|
|
|
26
30
|
background: oklch(from var(--oc-primary) l c h / 0.1);
|
|
27
31
|
border-color: oklch(from var(--oc-primary) l c h / 0.35);
|
|
28
32
|
}
|
|
29
|
-
|
|
30
|
-
.oc-data-table-
|
|
31
|
-
.oc-data-table-
|
|
33
|
+
/* In the toolbar the trash pills read as one switch: the chosen one is filled. */
|
|
34
|
+
.oc-data-table-toolbar .oc-filter-pills { border-inline-start: 0; padding-inline-start: 0; }
|
|
35
|
+
.oc-data-table-toolbar .oc-filter-pill[aria-current="true"] {
|
|
36
|
+
color: var(--oc-primary-foreground);
|
|
37
|
+
background: var(--oc-primary);
|
|
38
|
+
border-color: var(--oc-primary);
|
|
39
|
+
}
|
|
32
40
|
|
|
33
41
|
/* The table sits in its own rounded frame. The scroll box keeps the header sticky. */
|
|
34
42
|
.oc-data-table-frame {
|
|
43
|
+
min-inline-size: 0;
|
|
35
44
|
overflow: hidden;
|
|
36
|
-
background: var(--oc-
|
|
45
|
+
background: var(--oc-card);
|
|
37
46
|
border: 1px solid var(--oc-border);
|
|
38
47
|
border-radius: calc(var(--oc-radius) - 2px);
|
|
39
48
|
}
|
|
40
49
|
.oc-data-table-scroll { overflow: auto; max-block-size: var(--oc-data-table-max, none); }
|
|
41
50
|
.oc-data-table .oc-table :is(th, td) { border-inline: 0; border-block-start: 0; }
|
|
51
|
+
/* Columns size to their content and cells stay on one line: a narrow screen scrolls the frame sideways instead of
|
|
52
|
+
squeezing names and phone numbers onto two lines. .oc-data-table-wrap on a cell lets long text wrap again. */
|
|
53
|
+
.oc-data-table .oc-data-table-table { table-layout: auto; }
|
|
54
|
+
.oc-data-table .oc-data-table-table :is(th, td) { white-space: nowrap; vertical-align: middle; padding-inline: 0.625rem; }
|
|
55
|
+
.oc-data-table .oc-data-table-table td.oc-data-table-wrap { white-space: normal; }
|
|
42
56
|
.oc-data-table .oc-table thead th {
|
|
43
57
|
position: sticky;
|
|
44
58
|
inset-block-start: 0;
|
|
45
59
|
z-index: 1;
|
|
46
|
-
block-size: 2.
|
|
47
|
-
font-size: 0.
|
|
48
|
-
font-weight:
|
|
60
|
+
block-size: 2.75rem;
|
|
61
|
+
font-size: 0.8125rem;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
color: var(--oc-foreground);
|
|
49
64
|
background: var(--oc-card);
|
|
50
65
|
}
|
|
51
|
-
|
|
66
|
+
/* Striped rows (every second visible row), the row under the pointer a step stronger. Solid colors, so the sticky
|
|
67
|
+
actions cell can take the same background (inherit) and nothing scrolls through it. */
|
|
68
|
+
.oc-data-table {
|
|
69
|
+
--oc-data-table-stripe: color-mix(in oklch, var(--oc-card), var(--oc-foreground) 3.5%);
|
|
70
|
+
--oc-data-table-hover: color-mix(in oklch, var(--oc-card), var(--oc-primary) 8%);
|
|
71
|
+
}
|
|
72
|
+
.oc-data-table .oc-table tbody tr:nth-child(even of :not([hidden])) { background: var(--oc-data-table-stripe); }
|
|
73
|
+
.oc-data-table .oc-table tbody tr:hover { background: var(--oc-data-table-hover); }
|
|
74
|
+
.oc-data-table--plain .oc-table tbody tr:nth-child(even of :not([hidden])):not(:hover) { background: none; }
|
|
52
75
|
.oc-data-table .oc-table tbody tr:last-child > td { border-block-end: 0; }
|
|
53
76
|
|
|
54
|
-
/* Header label
|
|
55
|
-
.oc-data-table-th { display: flex; align-items: center; gap: 0.25rem;
|
|
56
|
-
.oc-data-table-resize {
|
|
57
|
-
position: absolute;
|
|
58
|
-
inset-block: 0;
|
|
59
|
-
inset-inline-end: -0.45rem;
|
|
60
|
-
inline-size: 0.55rem;
|
|
61
|
-
padding: 0;
|
|
62
|
-
background: none;
|
|
63
|
-
border: 0;
|
|
64
|
-
cursor: col-resize;
|
|
65
|
-
touch-action: none;
|
|
66
|
-
}
|
|
67
|
-
.oc-data-table-resize::after {
|
|
68
|
-
content: "";
|
|
69
|
-
position: absolute;
|
|
70
|
-
inset-block: 0.4rem;
|
|
71
|
-
inset-inline-start: 0.25rem;
|
|
72
|
-
inline-size: 1px;
|
|
73
|
-
background: var(--oc-border);
|
|
74
|
-
}
|
|
75
|
-
.oc-data-table.is-resizing { cursor: col-resize; user-select: none; }
|
|
76
|
-
/* After a resize the table grows with its columns and the frame scrolls sideways. */
|
|
77
|
-
.oc-data-table-table--sized { inline-size: max-content; min-inline-size: 100%; }
|
|
77
|
+
/* Header label (column width by dragging is OmniaUI Pro: app-data-table-pro.css). */
|
|
78
|
+
.oc-data-table-th { display: flex; align-items: center; gap: 0.25rem; min-inline-size: 0; }
|
|
78
79
|
|
|
79
80
|
/* Narrow control columns. The actions column stays on the right while the rest scrolls. */
|
|
80
81
|
.oc-data-table .oc-table :is(th, td)[data-oc-col="select"],
|
|
@@ -90,8 +91,8 @@
|
|
|
90
91
|
background: var(--oc-card);
|
|
91
92
|
box-shadow: -1px 0 0 var(--oc-border);
|
|
92
93
|
}
|
|
93
|
-
.oc-data-table .oc-table thead th[data-oc-col="actions"] { z-index: 3; }
|
|
94
|
-
.oc-data-table .oc-table tbody tr:nth-child(even) > td[data-oc-col="actions"] { background:
|
|
94
|
+
.oc-data-table .oc-table thead th[data-oc-col="actions"] { z-index: 3; background: var(--oc-card); }
|
|
95
|
+
.oc-data-table .oc-table tbody tr:is(:nth-child(even of :not([hidden])), :hover) > td[data-oc-col="actions"] { background: inherit; }
|
|
95
96
|
|
|
96
97
|
/* Trash and archive rows stay readable but quieter than active rows. */
|
|
97
98
|
.oc-data-table tr[data-oc-status="deleted"],
|
|
@@ -120,55 +121,24 @@
|
|
|
120
121
|
padding: 0.75rem;
|
|
121
122
|
}
|
|
122
123
|
.oc-data-table-card {
|
|
123
|
-
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
gap: 0.75rem;
|
|
127
|
+
padding: 0.875rem;
|
|
124
128
|
background: var(--oc-card);
|
|
125
129
|
border: 1px solid var(--oc-border);
|
|
126
130
|
border-radius: calc(var(--oc-radius) - 2px);
|
|
127
131
|
}
|
|
128
132
|
.oc-data-table-card-title { margin: 0; font-size: 0.875rem; font-weight: 600; }
|
|
129
|
-
|
|
133
|
+
.oc-data-table-card-title .oc-data-table-name-sub { font-weight: 400; }
|
|
130
134
|
.oc-data-table-empty { margin: 0; padding: 2rem 1rem; text-align: center; color: var(--oc-muted-foreground); }
|
|
131
135
|
.oc-data-table-empty[hidden] { display: none; }
|
|
132
136
|
|
|
133
|
-
/* Footer: count on the left, rows-per-page in the middle, pager on the right. */
|
|
134
|
-
.oc-data-table-footer {
|
|
135
|
-
display: grid;
|
|
136
|
-
grid-template-columns: 1fr auto 1fr;
|
|
137
|
-
align-items: center;
|
|
138
|
-
gap: 0.75rem;
|
|
139
|
-
}
|
|
140
|
-
.oc-data-table-summary { margin: 0; font-size: 0.8125rem; color: var(--oc-muted-foreground); }
|
|
141
|
-
.oc-data-table-per-page { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; }
|
|
142
|
-
.oc-data-table-per-page .oc-select { inline-size: auto; }
|
|
143
|
-
.oc-data-table-pager { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.25rem; }
|
|
144
|
-
.oc-data-table-page {
|
|
145
|
-
min-inline-size: 2rem;
|
|
146
|
-
padding: 0.25rem 0.45rem;
|
|
147
|
-
font: inherit;
|
|
148
|
-
font-size: 0.8125rem;
|
|
149
|
-
color: var(--oc-foreground);
|
|
150
|
-
background: var(--oc-card);
|
|
151
|
-
border: 1px solid var(--oc-border);
|
|
152
|
-
border-radius: calc(var(--oc-radius) - 2px);
|
|
153
|
-
cursor: pointer;
|
|
154
|
-
}
|
|
155
|
-
.oc-data-table-page[aria-current="page"] {
|
|
156
|
-
color: var(--oc-primary);
|
|
157
|
-
background: oklch(from var(--oc-primary) l c h / 0.1);
|
|
158
|
-
border-color: oklch(from var(--oc-primary) l c h / 0.35);
|
|
159
|
-
}
|
|
160
|
-
.oc-data-table-page:disabled { opacity: 0.4; cursor: default; }
|
|
161
|
-
.oc-data-table-page:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
|
|
162
|
-
.oc-data-table-ellipsis { padding-inline: 0.25rem; color: var(--oc-muted-foreground); }
|
|
163
137
|
|
|
164
138
|
/* Design choice from the gear menu: tighter rows, like the compact article lists. */
|
|
165
139
|
.oc-data-table--compact .oc-table :is(th, td) { padding: 0.25rem 0.5rem; font-size: 0.8125rem; }
|
|
166
140
|
.oc-data-table--compact .oc-table thead th { block-size: 2rem; }
|
|
167
141
|
|
|
168
|
-
@media (max-width: 40rem) {
|
|
169
|
-
.oc-data-table-footer { grid-template-columns: 1fr; }
|
|
170
|
-
.oc-data-table-pager { justify-content: flex-start; }
|
|
171
|
-
}
|
|
172
142
|
@media (prefers-reduced-motion: reduce) {
|
|
173
143
|
.oc-data-table .oc-table tbody tr { transition: none; }
|
|
174
144
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/app-name-card.css | Map: card (details) -> head (chevron, name, rename, amount, actions) -> rename input -> body -> nested cards */
|
|
2
|
+
/* Name card: a collapsible card for one part of a quote or plan,
|
|
3
|
+
with its name, a rename pencil, an optional amount and a delete button. Native <details>: it opens without script;
|
|
4
|
+
packages/ui/src/name-card.js adds the rename (pencil -> input, Enter saves, Escape cancels). */
|
|
5
|
+
|
|
6
|
+
.oc-name-card {
|
|
7
|
+
background: var(--oc-card);
|
|
8
|
+
border: 1px solid var(--oc-border);
|
|
9
|
+
border-radius: var(--oc-radius);
|
|
10
|
+
}
|
|
11
|
+
.oc-name-card + .oc-name-card { margin-block-start: 0.5rem; }
|
|
12
|
+
.oc-name-card__head {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 0.5rem;
|
|
16
|
+
min-block-size: 2.75rem;
|
|
17
|
+
padding: 0.375rem 0.5rem 0.375rem 0.75rem;
|
|
18
|
+
list-style: none;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
}
|
|
21
|
+
.oc-name-card__head::-webkit-details-marker { display: none; }
|
|
22
|
+
.oc-name-card__head::before {
|
|
23
|
+
content: "";
|
|
24
|
+
flex-shrink: 0;
|
|
25
|
+
inline-size: 1rem;
|
|
26
|
+
block-size: 1rem;
|
|
27
|
+
background: var(--oc-muted-foreground);
|
|
28
|
+
-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;
|
|
29
|
+
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;
|
|
30
|
+
transition: rotate var(--oc-transition-fast);
|
|
31
|
+
}
|
|
32
|
+
.oc-name-card[open] > .oc-name-card__head::before { rotate: 90deg; }
|
|
33
|
+
.oc-name-card__head:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; border-radius: var(--oc-radius); }
|
|
34
|
+
.oc-name-card__title { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
|
|
35
|
+
.oc-name-card__meta { font-size: 0.75rem; color: var(--oc-muted-foreground); white-space: nowrap; }
|
|
36
|
+
.oc-name-card__head .oc-btn { flex-shrink: 0; }
|
|
37
|
+
/* Amount and actions sit on the right; the amount is tabular so a column of cards lines up. */
|
|
38
|
+
.oc-name-card__amount { margin-inline-start: auto; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
39
|
+
.oc-name-card__actions { display: inline-flex; flex-shrink: 0; gap: 0.25rem; margin-inline-start: auto; }
|
|
40
|
+
.oc-name-card__amount + .oc-name-card__actions { margin-inline-start: 0.5rem; }
|
|
41
|
+
|
|
42
|
+
/* Rename: the title becomes an input of the same size (hidden toggles between the two). */
|
|
43
|
+
.oc-name-card__input {
|
|
44
|
+
flex: 1;
|
|
45
|
+
min-inline-size: 0;
|
|
46
|
+
block-size: 2rem;
|
|
47
|
+
padding: 0 0.5rem;
|
|
48
|
+
font: inherit;
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
color: var(--oc-foreground);
|
|
51
|
+
background: var(--oc-background);
|
|
52
|
+
border: 1px solid var(--oc-ring);
|
|
53
|
+
border-radius: calc(var(--oc-radius) - 2px);
|
|
54
|
+
}
|
|
55
|
+
.oc-name-card__input[hidden],
|
|
56
|
+
.oc-name-card__title[hidden] { display: none; }
|
|
57
|
+
|
|
58
|
+
.oc-name-card__body { padding: 0.75rem 0.75rem 0.875rem 2.25rem; border-block-start: 1px solid var(--oc-border); }
|
|
59
|
+
/* Nested cards (the parts of a title) use a quieter surface; --total colors the amount of the main card. */
|
|
60
|
+
.oc-name-card__body > .oc-name-card { background: var(--oc-surface); }
|
|
61
|
+
.oc-name-card--total > .oc-name-card__head .oc-name-card__amount { color: var(--oc-primary); }
|
|
62
|
+
|
|
63
|
+
@media (prefers-reduced-motion: reduce) {
|
|
64
|
+
.oc-name-card__head::before { transition: none; }
|
|
65
|
+
}
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
min-block-size: 0;
|
|
11
11
|
timeline-scope: --oc-titlebar-scroll;
|
|
12
12
|
}
|
|
13
|
+
/* A card with a title bar on top: the bar runs edge to edge, its last button follows the card corner. */
|
|
14
|
+
.oc-card:has(> .oc-titlebar:first-child) { padding: 0; --oc-titlebar-radius: calc(var(--oc-radius) + 3px); }
|
|
15
|
+
/* As the heading of a card section (bar + .oc-card-content): the title lines up with the content below. */
|
|
16
|
+
.oc-card > .oc-titlebar:first-child:has(+ .oc-card-content) { padding-inline-start: 24px; }
|
|
17
|
+
.oc-card > .oc-titlebar:first-child:has(+ .oc-card-content) .oc-titlebar__title { font-size: 0.9375rem; }
|
|
13
18
|
|
|
14
19
|
.oc-titlebar {
|
|
15
20
|
display: flex;
|
|
@@ -17,7 +22,7 @@
|
|
|
17
22
|
align-items: center;
|
|
18
23
|
gap: 2px;
|
|
19
24
|
min-block-size: 46px;
|
|
20
|
-
padding: 0
|
|
25
|
+
padding: 0 0 0 14px;
|
|
21
26
|
border-block-end: 1px solid transparent;
|
|
22
27
|
-webkit-user-select: none;
|
|
23
28
|
user-select: none;
|
|
@@ -34,7 +39,7 @@
|
|
|
34
39
|
/* Title area: grows, cuts the subtitle off with …. */
|
|
35
40
|
.oc-titlebar__main { display: flex; flex: 1; align-items: center; gap: 8px; min-inline-size: 0; }
|
|
36
41
|
.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; }
|
|
42
|
+
.oc-titlebar__title { flex: none; margin: 0; font-size: 0.8667rem; font-weight: 650; letter-spacing: -0.01em; }
|
|
38
43
|
.oc-titlebar__subtitle {
|
|
39
44
|
min-inline-size: 0;
|
|
40
45
|
overflow: hidden;
|
|
@@ -44,17 +49,19 @@
|
|
|
44
49
|
white-space: nowrap;
|
|
45
50
|
}
|
|
46
51
|
.oc-titlebar__subtitle::before { content: "·"; margin-inline-end: 8px; }
|
|
47
|
-
|
|
52
|
+
/* Window buttons sit in the top right corner (as on a desktop window), not centred on the title line. */
|
|
53
|
+
.oc-titlebar__actions { display: flex; flex: none; align-self: flex-start; align-items: flex-start; margin-inline-start: auto; }
|
|
48
54
|
|
|
49
|
-
/*
|
|
55
|
+
/* Window buttons: muted, a light surface on hover, the close button red. The last one follows the
|
|
56
|
+
rounded corner of the card or window around the bar (--oc-titlebar-radius). */
|
|
50
57
|
.oc-titlebar__btn {
|
|
51
58
|
display: inline-grid;
|
|
52
59
|
place-items: center;
|
|
53
|
-
inline-size:
|
|
54
|
-
block-size:
|
|
60
|
+
inline-size: 40px;
|
|
61
|
+
block-size: 34px;
|
|
55
62
|
padding: 0;
|
|
56
63
|
border: 0;
|
|
57
|
-
border-radius:
|
|
64
|
+
border-radius: 0;
|
|
58
65
|
background: transparent;
|
|
59
66
|
color: var(--oc-muted-foreground);
|
|
60
67
|
font: inherit;
|
|
@@ -65,6 +72,7 @@
|
|
|
65
72
|
.oc-titlebar__btn:hover { background: var(--oc-glass-panel-strong, var(--oc-accent)); color: var(--oc-foreground); }
|
|
66
73
|
.oc-titlebar__btn:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
|
|
67
74
|
.oc-titlebar__btn--close:hover { background: var(--oc-destructive); color: var(--oc-destructive-foreground); }
|
|
75
|
+
.oc-titlebar__actions > .oc-titlebar__btn:last-child { border-start-end-radius: var(--oc-titlebar-radius, calc(var(--oc-radius) - 1px)); }
|
|
68
76
|
|
|
69
77
|
/* Body: the part that scrolls under the bar. */
|
|
70
78
|
.oc-titlebar-body {
|
|
@@ -89,7 +97,7 @@
|
|
|
89
97
|
|
|
90
98
|
/* Sizes */
|
|
91
99
|
.oc-titlebar--sm { min-block-size: 36px; padding-inline-start: 10px; }
|
|
92
|
-
.oc-titlebar--sm .oc-titlebar__btn { inline-size:
|
|
100
|
+
.oc-titlebar--sm .oc-titlebar__btn { inline-size: 34px; block-size: 28px; }
|
|
93
101
|
|
|
94
102
|
@media (prefers-reduced-motion: reduce) {
|
|
95
103
|
.oc-titlebar-window > .oc-titlebar { animation: none; }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/data-meter-row.css | Map: row grid -> label and value -> bar and fill -> levels -> reset line -> list */
|
|
2
|
+
/* Usage meter row: a label, the usage as text, a bar and when it resets. The fill comes from
|
|
3
|
+
--oc-meter (0–100%); data-level="warn" or "critical" changes the color. */
|
|
4
|
+
|
|
5
|
+
.oc-meter-row {
|
|
6
|
+
display: grid;
|
|
7
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
8
|
+
align-items: baseline;
|
|
9
|
+
gap: 0.375rem 0.75rem;
|
|
10
|
+
}
|
|
11
|
+
.oc-meter-row__label { min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.875rem; font-weight: 600; }
|
|
12
|
+
.oc-meter-row__value { font-size: 0.8125rem; font-variant-numeric: tabular-nums; color: var(--oc-muted-foreground); white-space: nowrap; }
|
|
13
|
+
.oc-meter-row__value strong { color: var(--oc-foreground); }
|
|
14
|
+
.oc-meter-row__bar {
|
|
15
|
+
grid-column: 1 / -1;
|
|
16
|
+
block-size: 0.5rem;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
background: var(--oc-muted);
|
|
19
|
+
border-radius: 999px;
|
|
20
|
+
}
|
|
21
|
+
.oc-meter-row__bar::before {
|
|
22
|
+
content: "";
|
|
23
|
+
display: block;
|
|
24
|
+
inline-size: clamp(0%, var(--oc-meter, 0%), 100%);
|
|
25
|
+
block-size: 100%;
|
|
26
|
+
background: var(--oc-meter-color, var(--oc-success));
|
|
27
|
+
border-radius: inherit;
|
|
28
|
+
transition: inline-size 0.4s ease;
|
|
29
|
+
}
|
|
30
|
+
.oc-meter-row[data-level="warn"] { --oc-meter-color: var(--oc-warning); }
|
|
31
|
+
.oc-meter-row[data-level="critical"] { --oc-meter-color: var(--oc-destructive); }
|
|
32
|
+
.oc-meter-row[data-level="critical"] .oc-meter-row__value strong { color: var(--oc-destructive); }
|
|
33
|
+
.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); }
|
|
34
|
+
.oc-meter-list { display: grid; gap: 1rem; }
|
|
35
|
+
.oc-meter-list--compact { gap: 0.625rem; }
|
|
36
|
+
.oc-meter-list--compact .oc-meter-row__bar { block-size: 0.375rem; }
|
|
37
|
+
|
|
38
|
+
@media (prefers-reduced-motion: reduce) {
|
|
39
|
+
.oc-meter-row__bar::before { transition: none; }
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/data-price.css | Map: price block -> main line (amount, old price, discount) -> VAT note and alternative -> sizes and sale */
|
|
2
|
+
/* A price as shops and quotes show it: the amount, an optional old price and discount badge, a VAT note
|
|
3
|
+
("excl. VAT") and the other amount ("€ 121,00 incl. VAT"). Numbers use tabular digits. */
|
|
4
|
+
|
|
5
|
+
.oc-price { display: inline-grid; gap: 0.125rem; min-inline-size: 0; }
|
|
6
|
+
.oc-price__main { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; }
|
|
7
|
+
.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; }
|
|
8
|
+
.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; }
|
|
9
|
+
.oc-price__main > .oc-badge { align-self: center; }
|
|
10
|
+
.oc-price__note { font-size: 0.75rem; font-weight: 500; color: var(--oc-muted-foreground); }
|
|
11
|
+
.oc-price__alt { font-size: 0.8125rem; color: var(--oc-muted-foreground); font-variant-numeric: tabular-nums; }
|
|
12
|
+
|
|
13
|
+
.oc-price--sm .oc-price__amount { font-size: 1rem; }
|
|
14
|
+
.oc-price--lg .oc-price__amount { font-size: 1.75rem; }
|
|
15
|
+
.oc-price--sale .oc-price__amount { color: var(--oc-destructive); }
|
|
16
|
+
/* Inline: amount and note on one line (table cells, lists). */
|
|
17
|
+
.oc-price--inline { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; }
|
|
18
|
+
.oc-price--inline .oc-price__amount { font-size: inherit; font-weight: 600; }
|
|
19
|
+
td[data-numeric] > .oc-price, .oc-price--end { justify-items: end; text-align: end; }
|
|
20
|
+
td[data-numeric] > .oc-price .oc-price__main, .oc-price--end .oc-price__main { justify-content: flex-end; }
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/data-ribbon.css | Map: host (positioned, clipped) -> ribbon band -> four corners -> tones -> size -> RTL */
|
|
2
|
+
/* Corner ribbon: a diagonal band across one corner of a card, an image or a document ("Paid", "New", "Sale").
|
|
3
|
+
Put .oc-ribbon as a child of the box; the box gets position: relative and clips the band (zero specificity,
|
|
4
|
+
so your own position or overflow wins). The text is real text, so screen readers read it. */
|
|
5
|
+
|
|
6
|
+
:where(:has(> .oc-ribbon)) { position: relative; overflow: clip; }
|
|
7
|
+
|
|
8
|
+
.oc-ribbon {
|
|
9
|
+
/* Distance from the corner to the middle of the band, along both edges. */
|
|
10
|
+
--oc-ribbon-inset: 2.25rem;
|
|
11
|
+
--oc-ribbon-x: 1;
|
|
12
|
+
--oc-ribbon-y: -1;
|
|
13
|
+
--oc-ribbon-turn: 45deg;
|
|
14
|
+
--oc-ribbon-bg: var(--oc-primary);
|
|
15
|
+
--oc-ribbon-fg: var(--oc-primary-foreground);
|
|
16
|
+
position: absolute;
|
|
17
|
+
z-index: 2;
|
|
18
|
+
inset-block-start: var(--oc-ribbon-inset);
|
|
19
|
+
inset-inline-end: var(--oc-ribbon-inset);
|
|
20
|
+
inline-size: calc(var(--oc-ribbon-inset) * 2.83 + 3rem);
|
|
21
|
+
padding: 0.3em 0;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
color: var(--oc-ribbon-fg);
|
|
24
|
+
background: var(--oc-ribbon-bg);
|
|
25
|
+
box-shadow: 0 1px 3px oklch(0 0 0 / 0.18);
|
|
26
|
+
font-size: 0.75rem;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
line-height: 1.25;
|
|
29
|
+
letter-spacing: 0.08em;
|
|
30
|
+
text-align: center;
|
|
31
|
+
text-overflow: ellipsis;
|
|
32
|
+
text-transform: uppercase;
|
|
33
|
+
white-space: nowrap;
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
translate: calc(50% * var(--oc-ribbon-x)) calc(50% * var(--oc-ribbon-y));
|
|
36
|
+
rotate: var(--oc-ribbon-turn);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Corners: top end is the default. */
|
|
40
|
+
.oc-ribbon--top-start {
|
|
41
|
+
inset-inline-end: auto;
|
|
42
|
+
inset-inline-start: var(--oc-ribbon-inset);
|
|
43
|
+
--oc-ribbon-x: -1;
|
|
44
|
+
--oc-ribbon-turn: -45deg;
|
|
45
|
+
}
|
|
46
|
+
.oc-ribbon--bottom-end {
|
|
47
|
+
inset-block-start: auto;
|
|
48
|
+
inset-block-end: var(--oc-ribbon-inset);
|
|
49
|
+
--oc-ribbon-y: 1;
|
|
50
|
+
--oc-ribbon-turn: -45deg;
|
|
51
|
+
}
|
|
52
|
+
.oc-ribbon--bottom-start {
|
|
53
|
+
inset-block-start: auto;
|
|
54
|
+
inset-block-end: var(--oc-ribbon-inset);
|
|
55
|
+
inset-inline-end: auto;
|
|
56
|
+
inset-inline-start: var(--oc-ribbon-inset);
|
|
57
|
+
--oc-ribbon-x: -1;
|
|
58
|
+
--oc-ribbon-y: 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Tones. */
|
|
62
|
+
.oc-ribbon--success { --oc-ribbon-bg: var(--oc-success); --oc-ribbon-fg: var(--oc-success-foreground); }
|
|
63
|
+
.oc-ribbon--warning { --oc-ribbon-bg: var(--oc-warning); --oc-ribbon-fg: var(--oc-warning-foreground); }
|
|
64
|
+
.oc-ribbon--destructive { --oc-ribbon-bg: var(--oc-destructive); --oc-ribbon-fg: var(--oc-destructive-foreground); }
|
|
65
|
+
.oc-ribbon--muted { --oc-ribbon-bg: var(--oc-muted); --oc-ribbon-fg: var(--oc-muted-foreground); }
|
|
66
|
+
|
|
67
|
+
/* Sizes: a smaller band for a product card, a larger one for a document or a hero image. */
|
|
68
|
+
.oc-ribbon--sm { --oc-ribbon-inset: 1.6rem; font-size: 0.625rem; }
|
|
69
|
+
.oc-ribbon--lg { --oc-ribbon-inset: 2.9rem; font-size: 0.875rem; }
|
|
70
|
+
|
|
71
|
+
/* Right to left: start and end swap sides, so the band leans the other way. */
|
|
72
|
+
.oc-ribbon:dir(rtl) { translate: calc(-50% * var(--oc-ribbon-x)) calc(50% * var(--oc-ribbon-y)); rotate: calc(var(--oc-ribbon-turn) * -1); }
|
|
73
|
+
|
|
74
|
+
@media (forced-colors: active) {
|
|
75
|
+
.oc-ribbon { forced-color-adjust: none; color: Canvas; background: CanvasText; }
|
|
76
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* File: packages/ui/src/parts/data-spec-list.css | Map: list (container) -> groups in columns -> group title -> rows (term / value) -> stack modifier */
|
|
2
|
+
/* Technical specifications in named groups (Dimensions, Electrical, Package …): label / value rows with tabular
|
|
3
|
+
digits. Two columns of groups once the list is wide enough (container query), one column with --stack. */
|
|
4
|
+
|
|
5
|
+
.oc-spec-list { container-type: inline-size; inline-size: 100%; display: grid; gap: 1.25rem; min-inline-size: 0; }
|
|
6
|
+
@container (min-width: 40rem) {
|
|
7
|
+
.oc-spec-list:not(.oc-spec-list--stack) { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: 1.25rem 2rem; }
|
|
8
|
+
}
|
|
9
|
+
.oc-spec-group { min-inline-size: 0; }
|
|
10
|
+
.oc-spec-group__title {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: 0.5rem;
|
|
14
|
+
margin: 0 0 0.25rem;
|
|
15
|
+
padding-block-end: 0.375rem;
|
|
16
|
+
font-size: 0.75rem;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
letter-spacing: 0.04em;
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
color: var(--oc-muted-foreground);
|
|
21
|
+
border-block-end: 1px solid var(--oc-border);
|
|
22
|
+
}
|
|
23
|
+
.oc-spec-group__title > :is(svg, iconify-icon) { inline-size: 1rem; block-size: 1rem; font-size: 1rem; }
|
|
24
|
+
.oc-spec-group__rows { margin: 0; }
|
|
25
|
+
.oc-spec-row {
|
|
26
|
+
display: grid;
|
|
27
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
|
|
28
|
+
gap: 1rem;
|
|
29
|
+
padding: 0.4rem 0;
|
|
30
|
+
font-size: 0.875rem;
|
|
31
|
+
border-block-end: 1px dashed var(--oc-border);
|
|
32
|
+
}
|
|
33
|
+
.oc-spec-row:last-child { border-block-end: 0; }
|
|
34
|
+
.oc-spec-row > dt { margin: 0; color: var(--oc-muted-foreground); }
|
|
35
|
+
.oc-spec-row > dd { margin: 0; min-inline-size: 0; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
|
|
36
|
+
.oc-spec-row > dd > small { margin-inline-start: 0.25rem; font-weight: 400; color: var(--oc-muted-foreground); }
|