@onyxdesign/core 1.0.0 → 1.1.1
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/package.json +2 -2
- package/src/onyx.css +278 -15
- package/src/onyx.js +127 -6
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**The interface, in black & white.**
|
|
6
6
|
|
|
7
|
-
[Website](https://
|
|
7
|
+
[Website](https://onyxdesign.vercel.app/) · [Documentation](https://onyxdesign.vercel.app/docs/) · [Pricing](https://onyxdesign.vercel.app/#pricing)
|
|
8
8
|
|
|
9
9
|
Onyx is a zero-dependency UI component library for designers who believe restraint is a feature. Two colors, sharp typography, and every detail considered. One CSS file, one small script, light and dark themes built in.
|
|
10
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onyxdesign/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A black & white, zero-dependency UI component library for people who care about the details.",
|
|
5
5
|
"main": "src/onyx.js",
|
|
6
6
|
"style": "src/onyx.css",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"type": "git",
|
|
29
29
|
"url": "git+https://github.com/codewithanshuman/Onyx.git"
|
|
30
30
|
},
|
|
31
|
-
"homepage": "https://
|
|
31
|
+
"homepage": "https://onyxdesign.vercel.app/",
|
|
32
32
|
"bugs": {
|
|
33
33
|
"url": "https://github.com/codewithanshuman/Onyx/issues"
|
|
34
34
|
},
|
package/src/onyx.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Onyx v1.
|
|
2
|
+
* Onyx v1.1.1 — a black & white component library for the web
|
|
3
3
|
* Core: MIT License. Onyx Pro components require a commercial license.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
--ox-surface: #ffffff;
|
|
20
20
|
--ox-border: #e8e8e8;
|
|
21
21
|
--ox-border-strong: #d4d4d4;
|
|
22
|
+
--ox-control-border: #8f8f8f;
|
|
22
23
|
--ox-fg: #0a0a0a;
|
|
23
|
-
--ox-fg-muted: #
|
|
24
|
-
--ox-fg-subtle: #
|
|
24
|
+
--ox-fg-muted: #5c5c5c;
|
|
25
|
+
--ox-fg-subtle: #737373;
|
|
25
26
|
--ox-inverse: #ffffff;
|
|
26
27
|
--ox-ring: rgba(10, 10, 10, 0.12);
|
|
27
28
|
--ox-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
@@ -38,9 +39,10 @@
|
|
|
38
39
|
--ox-surface: #0a0a0a;
|
|
39
40
|
--ox-border: #222222;
|
|
40
41
|
--ox-border-strong: #383838;
|
|
42
|
+
--ox-control-border: #5e5e5e;
|
|
41
43
|
--ox-fg: #fafafa;
|
|
42
|
-
--ox-fg-muted: #
|
|
43
|
-
--ox-fg-subtle: #
|
|
44
|
+
--ox-fg-muted: #a8a8a8;
|
|
45
|
+
--ox-fg-subtle: #8a8a8a;
|
|
44
46
|
--ox-inverse: #000000;
|
|
45
47
|
--ox-ring: rgba(255, 255, 255, 0.18);
|
|
46
48
|
--ox-shadow-sm: 0 0 0 1px rgba(255, 255, 255, 0.02);
|
|
@@ -61,9 +63,10 @@
|
|
|
61
63
|
--ox-surface: #0a0a0a;
|
|
62
64
|
--ox-border: #222222;
|
|
63
65
|
--ox-border-strong: #383838;
|
|
66
|
+
--ox-control-border: #5e5e5e;
|
|
64
67
|
--ox-fg: #fafafa;
|
|
65
|
-
--ox-fg-muted: #
|
|
66
|
-
--ox-fg-subtle: #
|
|
68
|
+
--ox-fg-muted: #a8a8a8;
|
|
69
|
+
--ox-fg-subtle: #8a8a8a;
|
|
67
70
|
--ox-inverse: #000000;
|
|
68
71
|
--ox-ring: rgba(255, 255, 255, 0.18);
|
|
69
72
|
--ox-shadow-sm: 0 0 0 1px rgba(255, 255, 255, 0.02);
|
|
@@ -78,9 +81,10 @@
|
|
|
78
81
|
--ox-surface: #ffffff;
|
|
79
82
|
--ox-border: #e8e8e8;
|
|
80
83
|
--ox-border-strong: #d4d4d4;
|
|
84
|
+
--ox-control-border: #8f8f8f;
|
|
81
85
|
--ox-fg: #0a0a0a;
|
|
82
|
-
--ox-fg-muted: #
|
|
83
|
-
--ox-fg-subtle: #
|
|
86
|
+
--ox-fg-muted: #5c5c5c;
|
|
87
|
+
--ox-fg-subtle: #737373;
|
|
84
88
|
--ox-inverse: #ffffff;
|
|
85
89
|
--ox-ring: rgba(10, 10, 10, 0.12);
|
|
86
90
|
--ox-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
@@ -341,7 +345,7 @@
|
|
|
341
345
|
font-size: var(--ox-text-sm);
|
|
342
346
|
color: var(--ox-fg);
|
|
343
347
|
background: var(--ox-surface);
|
|
344
|
-
border: 1px solid var(--ox-border);
|
|
348
|
+
border: 1px solid var(--ox-control-border);
|
|
345
349
|
border-radius: var(--ox-radius);
|
|
346
350
|
transition: border-color var(--ox-duration) var(--ox-ease), box-shadow var(--ox-duration) var(--ox-ease);
|
|
347
351
|
}
|
|
@@ -349,7 +353,7 @@
|
|
|
349
353
|
.ox-textarea::placeholder { color: var(--ox-fg-subtle); }
|
|
350
354
|
.ox-input:hover,
|
|
351
355
|
.ox-select:hover,
|
|
352
|
-
.ox-textarea:hover { border-color: var(--ox-
|
|
356
|
+
.ox-textarea:hover { border-color: var(--ox-fg-muted); }
|
|
353
357
|
.ox-input:focus,
|
|
354
358
|
.ox-select:focus,
|
|
355
359
|
.ox-textarea:focus {
|
|
@@ -403,7 +407,7 @@
|
|
|
403
407
|
height: 1rem;
|
|
404
408
|
margin: 0;
|
|
405
409
|
background: var(--ox-surface);
|
|
406
|
-
border: 1px solid var(--ox-border
|
|
410
|
+
border: 1px solid var(--ox-control-border);
|
|
407
411
|
cursor: pointer;
|
|
408
412
|
transition: background var(--ox-duration) var(--ox-ease), border-color var(--ox-duration) var(--ox-ease);
|
|
409
413
|
}
|
|
@@ -441,7 +445,7 @@
|
|
|
441
445
|
width: 2.25rem;
|
|
442
446
|
height: 1.25rem;
|
|
443
447
|
flex-shrink: 0;
|
|
444
|
-
background: var(--ox-border
|
|
448
|
+
background: var(--ox-control-border);
|
|
445
449
|
border-radius: var(--ox-radius-full);
|
|
446
450
|
transition: background var(--ox-duration) var(--ox-ease);
|
|
447
451
|
}
|
|
@@ -818,7 +822,7 @@
|
|
|
818
822
|
overflow-x: auto;
|
|
819
823
|
tab-size: 2;
|
|
820
824
|
}
|
|
821
|
-
.ox-code .c { color: #
|
|
825
|
+
.ox-code .c { color: #8a8a8a; } /* 5.6:1 on the code background */
|
|
822
826
|
.ox-code .t { color: #ffffff; }
|
|
823
827
|
.ox-code .a { color: #9a9a9a; }
|
|
824
828
|
.ox-code .s { color: #e5e5e5; font-style: italic; }
|
|
@@ -847,8 +851,84 @@
|
|
|
847
851
|
transition: color var(--ox-duration) var(--ox-ease), background var(--ox-duration) var(--ox-ease);
|
|
848
852
|
}
|
|
849
853
|
.ox-navbar-links a:hover { color: var(--ox-fg); background: var(--ox-bg-muted); }
|
|
854
|
+
|
|
855
|
+
/* Mobile menu: links collapse behind a toggle below 760px
|
|
856
|
+
(or below 1024px with data-collapse="lg" on .ox-navbar) */
|
|
857
|
+
.ox-navbar-toggle { display: none; }
|
|
858
|
+
.ox-navbar-menu { display: none; }
|
|
859
|
+
.ox-navbar-toggle {
|
|
860
|
+
position: relative;
|
|
861
|
+
width: 2.5rem;
|
|
862
|
+
height: 2.5rem;
|
|
863
|
+
padding: 0;
|
|
864
|
+
color: var(--ox-fg);
|
|
865
|
+
background: transparent;
|
|
866
|
+
border: 1px solid var(--ox-border);
|
|
867
|
+
border-radius: var(--ox-radius-full);
|
|
868
|
+
cursor: pointer;
|
|
869
|
+
}
|
|
870
|
+
.ox-navbar-toggle span {
|
|
871
|
+
position: absolute;
|
|
872
|
+
left: 50%;
|
|
873
|
+
top: 50%;
|
|
874
|
+
width: 1rem;
|
|
875
|
+
height: 1.5px;
|
|
876
|
+
margin: -0.75px 0 0 -0.5rem;
|
|
877
|
+
background: currentColor;
|
|
878
|
+
border-radius: 2px;
|
|
879
|
+
transition: transform 260ms var(--ox-ease);
|
|
880
|
+
}
|
|
881
|
+
.ox-navbar-toggle span:first-child { transform: translateY(-3.5px); }
|
|
882
|
+
.ox-navbar-toggle span:last-child { transform: translateY(3.5px); }
|
|
883
|
+
.ox-navbar[data-open="true"] .ox-navbar-toggle span:first-child { transform: rotate(45deg); }
|
|
884
|
+
.ox-navbar[data-open="true"] .ox-navbar-toggle span:last-child { transform: rotate(-45deg); }
|
|
885
|
+
/* Absolute, not fixed: the navbar's backdrop-filter makes it the containing block
|
|
886
|
+
for fixed children, so the menu is sized from the navbar's bottom edge instead */
|
|
887
|
+
.ox-navbar-menu {
|
|
888
|
+
position: absolute;
|
|
889
|
+
top: 100%;
|
|
890
|
+
left: 0;
|
|
891
|
+
right: 0;
|
|
892
|
+
height: calc(100vh - 3.75rem);
|
|
893
|
+
height: calc(100dvh - 3.75rem);
|
|
894
|
+
z-index: 99;
|
|
895
|
+
flex-direction: column;
|
|
896
|
+
gap: var(--ox-space-1);
|
|
897
|
+
padding: var(--ox-space-4) var(--ox-space-4) var(--ox-space-8);
|
|
898
|
+
overflow-y: auto;
|
|
899
|
+
background: var(--ox-bg);
|
|
900
|
+
border-top: 1px solid var(--ox-border);
|
|
901
|
+
opacity: 0;
|
|
902
|
+
transform: translateY(-8px);
|
|
903
|
+
visibility: hidden;
|
|
904
|
+
transition: opacity 220ms var(--ox-ease), transform 260ms var(--ox-ease), visibility 260ms;
|
|
905
|
+
}
|
|
906
|
+
.ox-navbar[data-open="true"] .ox-navbar-menu { opacity: 1; transform: none; visibility: visible; }
|
|
907
|
+
.ox-navbar-menu > a:not(.ox-btn) {
|
|
908
|
+
display: flex;
|
|
909
|
+
align-items: center;
|
|
910
|
+
justify-content: space-between;
|
|
911
|
+
min-height: 3.25rem;
|
|
912
|
+
padding: 0 var(--ox-space-2);
|
|
913
|
+
font-size: var(--ox-text-lg);
|
|
914
|
+
font-weight: 500;
|
|
915
|
+
letter-spacing: -0.01em;
|
|
916
|
+
color: var(--ox-fg);
|
|
917
|
+
text-decoration: none;
|
|
918
|
+
border-bottom: 1px solid var(--ox-border);
|
|
919
|
+
}
|
|
920
|
+
.ox-navbar-menu > a:not(.ox-btn)::after { content: "→"; color: var(--ox-fg-subtle); }
|
|
921
|
+
.ox-navbar-menu .ox-btn { margin-top: var(--ox-space-4); }
|
|
922
|
+
body.ox-nav-locked { overflow: hidden; }
|
|
850
923
|
@media (max-width: 760px) {
|
|
851
924
|
.ox-navbar-links { display: none; }
|
|
925
|
+
.ox-navbar-toggle { display: inline-grid; }
|
|
926
|
+
.ox-navbar-menu { display: flex; }
|
|
927
|
+
}
|
|
928
|
+
@media (max-width: 1023px) {
|
|
929
|
+
.ox-navbar[data-collapse="lg"] .ox-navbar-links { display: none; }
|
|
930
|
+
.ox-navbar[data-collapse="lg"] .ox-navbar-toggle { display: inline-grid; }
|
|
931
|
+
.ox-navbar[data-collapse="lg"] .ox-navbar-menu { display: flex; }
|
|
852
932
|
}
|
|
853
933
|
|
|
854
934
|
/* =========================================================
|
|
@@ -867,7 +947,186 @@
|
|
|
867
947
|
}
|
|
868
948
|
|
|
869
949
|
/* =========================================================
|
|
870
|
-
19.
|
|
950
|
+
19. Breadcrumb & pagination
|
|
951
|
+
========================================================= */
|
|
952
|
+
.ox-breadcrumb ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ox-space-2); margin: 0; padding: 0; list-style: none; font-size: var(--ox-text-sm); }
|
|
953
|
+
.ox-breadcrumb li { display: inline-flex; align-items: center; gap: var(--ox-space-2); color: var(--ox-fg-muted); }
|
|
954
|
+
.ox-breadcrumb li + li::before { content: "/"; color: var(--ox-fg-subtle); }
|
|
955
|
+
.ox-breadcrumb a { color: inherit; text-decoration: none; border-radius: var(--ox-radius-sm); transition: color var(--ox-duration); }
|
|
956
|
+
.ox-breadcrumb a:hover { color: var(--ox-fg); }
|
|
957
|
+
.ox-breadcrumb [aria-current="page"] { color: var(--ox-fg); font-weight: 500; }
|
|
958
|
+
|
|
959
|
+
.ox-pagination ul { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ox-space-1); margin: 0; padding: 0; list-style: none; }
|
|
960
|
+
.ox-pagination a,
|
|
961
|
+
.ox-pagination span {
|
|
962
|
+
display: inline-grid;
|
|
963
|
+
place-items: center;
|
|
964
|
+
min-width: 2.25rem;
|
|
965
|
+
height: 2.25rem;
|
|
966
|
+
padding: 0 var(--ox-space-2);
|
|
967
|
+
font-size: var(--ox-text-sm);
|
|
968
|
+
font-variant-numeric: tabular-nums;
|
|
969
|
+
color: var(--ox-fg-muted);
|
|
970
|
+
text-decoration: none;
|
|
971
|
+
border: 1px solid transparent;
|
|
972
|
+
border-radius: var(--ox-radius);
|
|
973
|
+
transition: background var(--ox-duration), color var(--ox-duration), border-color var(--ox-duration);
|
|
974
|
+
}
|
|
975
|
+
.ox-pagination a:hover { color: var(--ox-fg); background: var(--ox-bg-muted); }
|
|
976
|
+
.ox-pagination [aria-current="page"] { color: var(--ox-inverse); background: var(--ox-fg); border-color: var(--ox-fg); }
|
|
977
|
+
.ox-pagination [aria-disabled="true"] { opacity: 0.35; pointer-events: none; }
|
|
978
|
+
.ox-pagination .ox-gap { min-width: 1.5rem; }
|
|
979
|
+
|
|
980
|
+
/* =========================================================
|
|
981
|
+
20. Range slider & number stepper
|
|
982
|
+
========================================================= */
|
|
983
|
+
.ox-range {
|
|
984
|
+
--_pct: 50%;
|
|
985
|
+
-webkit-appearance: none;
|
|
986
|
+
appearance: none;
|
|
987
|
+
width: 100%;
|
|
988
|
+
height: 1.5rem;
|
|
989
|
+
margin: 0;
|
|
990
|
+
background: transparent;
|
|
991
|
+
cursor: pointer;
|
|
992
|
+
}
|
|
993
|
+
.ox-range::-webkit-slider-runnable-track { height: 4px; border-radius: 99px; background: linear-gradient(to right, var(--ox-fg) var(--_pct), var(--ox-bg-muted) var(--_pct)); }
|
|
994
|
+
.ox-range::-moz-range-track { height: 4px; border-radius: 99px; background: var(--ox-bg-muted); }
|
|
995
|
+
.ox-range::-moz-range-progress { height: 4px; border-radius: 99px; background: var(--ox-fg); }
|
|
996
|
+
.ox-range::-webkit-slider-thumb { -webkit-appearance: none; width: 1.25rem; height: 1.25rem; margin-top: calc(2px - 0.625rem); border-radius: 50%; background: var(--ox-bg); border: 2px solid var(--ox-fg); box-shadow: var(--ox-shadow-sm); transition: transform var(--ox-duration) var(--ox-ease); }
|
|
997
|
+
.ox-range::-moz-range-thumb { width: 1rem; height: 1rem; border-radius: 50%; background: var(--ox-bg); border: 2px solid var(--ox-fg); }
|
|
998
|
+
.ox-range:active::-webkit-slider-thumb { transform: scale(1.15); }
|
|
999
|
+
.ox-range:focus-visible { outline: none; }
|
|
1000
|
+
.ox-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px var(--ox-ring); }
|
|
1001
|
+
.ox-range:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px var(--ox-ring); }
|
|
1002
|
+
|
|
1003
|
+
.ox-stepper { display: inline-flex; align-items: stretch; height: 2.5rem; background: var(--ox-surface); border: 1px solid var(--ox-control-border); border-radius: var(--ox-radius); overflow: hidden; }
|
|
1004
|
+
.ox-stepper:focus-within { border-color: var(--ox-fg); box-shadow: 0 0 0 3px var(--ox-ring); }
|
|
1005
|
+
.ox-stepper button { width: 2.5rem; padding: 0; font-size: var(--ox-text-lg); line-height: 1; color: var(--ox-fg); background: transparent; border: 0; cursor: pointer; transition: background var(--ox-duration); }
|
|
1006
|
+
.ox-stepper button:hover { background: var(--ox-bg-muted); }
|
|
1007
|
+
.ox-stepper button:disabled { opacity: 0.3; cursor: not-allowed; background: transparent; }
|
|
1008
|
+
.ox-stepper input { width: 3.5rem; padding: 0; font: inherit; font-size: var(--ox-text-sm); font-variant-numeric: tabular-nums; text-align: center; color: var(--ox-fg); background: transparent; border: 0; border-inline: 1px solid var(--ox-border); outline: none; -moz-appearance: textfield; }
|
|
1009
|
+
.ox-stepper input::-webkit-outer-spin-button,
|
|
1010
|
+
.ox-stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
|
1011
|
+
|
|
1012
|
+
/* =========================================================
|
|
1013
|
+
21. Chips (toggleable filters)
|
|
1014
|
+
========================================================= */
|
|
1015
|
+
.ox-chips { display: flex; flex-wrap: wrap; gap: var(--ox-space-2); }
|
|
1016
|
+
.ox-chip {
|
|
1017
|
+
display: inline-flex;
|
|
1018
|
+
align-items: center;
|
|
1019
|
+
gap: 0.4rem;
|
|
1020
|
+
height: 2rem;
|
|
1021
|
+
padding: 0 var(--ox-space-3);
|
|
1022
|
+
font: 500 var(--ox-text-sm) var(--ox-font-sans);
|
|
1023
|
+
color: var(--ox-fg-muted);
|
|
1024
|
+
background: var(--ox-surface);
|
|
1025
|
+
border: 1px solid var(--ox-border);
|
|
1026
|
+
border-radius: var(--ox-radius-full);
|
|
1027
|
+
cursor: pointer;
|
|
1028
|
+
transition: color var(--ox-duration), background var(--ox-duration), border-color var(--ox-duration), transform var(--ox-duration) var(--ox-ease);
|
|
1029
|
+
}
|
|
1030
|
+
.ox-chip:hover { color: var(--ox-fg); border-color: var(--ox-border-strong); }
|
|
1031
|
+
.ox-chip:active { transform: scale(0.97); }
|
|
1032
|
+
.ox-chip[aria-pressed="true"] { color: var(--ox-inverse); background: var(--ox-fg); border-color: var(--ox-fg); }
|
|
1033
|
+
.ox-chip[aria-pressed="true"]::before { content: "✓"; font-size: 0.7rem; }
|
|
1034
|
+
|
|
1035
|
+
/* =========================================================
|
|
1036
|
+
22. Table
|
|
1037
|
+
========================================================= */
|
|
1038
|
+
.ox-table-wrap { overflow-x: auto; border: 1px solid var(--ox-border); border-radius: var(--ox-radius-lg); background: var(--ox-surface); }
|
|
1039
|
+
.ox-table { width: 100%; border-collapse: collapse; font-size: var(--ox-text-sm); font-variant-numeric: tabular-nums; }
|
|
1040
|
+
.ox-table caption { padding: var(--ox-space-3) var(--ox-space-4); text-align: left; color: var(--ox-fg-muted); caption-side: bottom; font-size: var(--ox-text-xs); }
|
|
1041
|
+
.ox-table th { padding: var(--ox-space-3) var(--ox-space-4); text-align: left; font-size: var(--ox-text-xs); font-weight: 500; color: var(--ox-fg-muted); background: var(--ox-bg-subtle); border-bottom: 1px solid var(--ox-border); white-space: nowrap; }
|
|
1042
|
+
.ox-table td { padding: var(--ox-space-3) var(--ox-space-4); border-bottom: 1px solid var(--ox-border); }
|
|
1043
|
+
.ox-table tbody tr:last-child td { border-bottom: 0; }
|
|
1044
|
+
.ox-table tbody tr { transition: background var(--ox-duration); }
|
|
1045
|
+
.ox-table tbody tr:hover { background: var(--ox-bg-subtle); }
|
|
1046
|
+
.ox-table .num { text-align: right; }
|
|
1047
|
+
.ox-table-striped tbody tr:nth-child(even) { background: var(--ox-bg-subtle); }
|
|
1048
|
+
|
|
1049
|
+
/* =========================================================
|
|
1050
|
+
23. Empty state & timeline
|
|
1051
|
+
========================================================= */
|
|
1052
|
+
.ox-empty { display: grid; justify-items: center; gap: var(--ox-space-2); padding: var(--ox-space-12) var(--ox-space-6); text-align: center; border: 1.5px dashed var(--ox-border-strong); border-radius: var(--ox-radius-lg); }
|
|
1053
|
+
.ox-empty-icon { display: grid; place-items: center; width: 3rem; height: 3rem; margin-bottom: var(--ox-space-2); font-size: var(--ox-text-xl); color: var(--ox-fg); background: var(--ox-bg-muted); border-radius: 50%; }
|
|
1054
|
+
.ox-empty-title { margin: 0; font-size: var(--ox-text-base); font-weight: 600; }
|
|
1055
|
+
.ox-empty-text { max-width: 36ch; margin: 0 0 var(--ox-space-3); font-size: var(--ox-text-sm); color: var(--ox-fg-muted); }
|
|
1056
|
+
|
|
1057
|
+
.ox-timeline { margin: 0; padding: 0; list-style: none; }
|
|
1058
|
+
.ox-timeline li { position: relative; padding: 0 0 var(--ox-space-6) var(--ox-space-8); }
|
|
1059
|
+
.ox-timeline li::before { content: ""; position: absolute; left: 0.3rem; top: 0.45rem; width: 0.65rem; height: 0.65rem; border-radius: 50%; background: var(--ox-bg); border: 2px solid var(--ox-fg); }
|
|
1060
|
+
.ox-timeline li::after { content: ""; position: absolute; left: calc(0.3rem + 0.325rem - 0.5px); top: 1.4rem; bottom: 0.2rem; width: 1px; background: var(--ox-border-strong); }
|
|
1061
|
+
.ox-timeline li:last-child { padding-bottom: 0; }
|
|
1062
|
+
.ox-timeline li:last-child::after { display: none; }
|
|
1063
|
+
.ox-timeline li[data-state="done"]::before { background: var(--ox-fg); }
|
|
1064
|
+
.ox-timeline li[data-state="upcoming"]::before { border-color: var(--ox-border-strong); border-style: dashed; }
|
|
1065
|
+
.ox-timeline time { display: block; margin-bottom: 2px; font: 400 var(--ox-text-xs) var(--ox-font-mono); color: var(--ox-fg-subtle); }
|
|
1066
|
+
.ox-timeline b { display: block; font-size: var(--ox-text-sm); font-weight: 500; }
|
|
1067
|
+
.ox-timeline p { margin: 2px 0 0; font-size: var(--ox-text-sm); color: var(--ox-fg-muted); }
|
|
1068
|
+
|
|
1069
|
+
/* =========================================================
|
|
1070
|
+
24. Banner (dismissible announcement)
|
|
1071
|
+
========================================================= */
|
|
1072
|
+
.ox-banner {
|
|
1073
|
+
position: relative;
|
|
1074
|
+
display: flex;
|
|
1075
|
+
flex-wrap: wrap;
|
|
1076
|
+
align-items: center;
|
|
1077
|
+
justify-content: center;
|
|
1078
|
+
gap: var(--ox-space-2) var(--ox-space-4);
|
|
1079
|
+
padding: var(--ox-space-2) 3rem;
|
|
1080
|
+
font-size: var(--ox-text-sm);
|
|
1081
|
+
text-align: center;
|
|
1082
|
+
color: var(--ox-inverse);
|
|
1083
|
+
background: var(--ox-fg);
|
|
1084
|
+
}
|
|
1085
|
+
.ox-banner a { color: inherit; font-weight: 500; text-underline-offset: 3px; }
|
|
1086
|
+
.ox-banner-close { position: absolute; right: var(--ox-space-2); top: 50%; display: grid; place-items: center; width: 2rem; height: 2rem; margin-top: -1rem; padding: 0; color: inherit; background: transparent; border: 0; border-radius: var(--ox-radius-sm); opacity: 0.7; cursor: pointer; }
|
|
1087
|
+
.ox-banner-close:hover { opacity: 1; background: color-mix(in srgb, var(--ox-inverse) 15%, transparent); }
|
|
1088
|
+
.ox-banner[hidden] { display: none; }
|
|
1089
|
+
|
|
1090
|
+
/* =========================================================
|
|
1091
|
+
25. Avatar status
|
|
1092
|
+
========================================================= */
|
|
1093
|
+
.ox-avatar[data-status] { position: relative; overflow: visible; }
|
|
1094
|
+
.ox-avatar[data-status]::after { content: ""; position: absolute; right: -1px; bottom: -1px; width: 28%; height: 28%; min-width: 8px; min-height: 8px; border-radius: 50%; border: 2px solid var(--ox-surface); }
|
|
1095
|
+
.ox-avatar[data-status="online"]::after { background: var(--ox-fg); }
|
|
1096
|
+
.ox-avatar[data-status="away"]::after { background: var(--ox-bg); box-shadow: inset 0 0 0 2px var(--ox-fg); }
|
|
1097
|
+
.ox-avatar[data-status="offline"]::after { background: var(--ox-border-strong); }
|
|
1098
|
+
|
|
1099
|
+
/* Skip link: hidden until a keyboard user tabs to it, then jumps past the navigation */
|
|
1100
|
+
.ox-skip {
|
|
1101
|
+
position: fixed;
|
|
1102
|
+
left: var(--ox-space-4);
|
|
1103
|
+
top: var(--ox-space-3);
|
|
1104
|
+
z-index: 1200;
|
|
1105
|
+
padding: var(--ox-space-2) var(--ox-space-4);
|
|
1106
|
+
font: 500 var(--ox-text-sm) var(--ox-font-sans);
|
|
1107
|
+
color: var(--ox-inverse);
|
|
1108
|
+
background: var(--ox-fg);
|
|
1109
|
+
border-radius: var(--ox-radius-full);
|
|
1110
|
+
text-decoration: none;
|
|
1111
|
+
transform: translateY(-200%);
|
|
1112
|
+
transition: transform var(--ox-duration) var(--ox-ease);
|
|
1113
|
+
}
|
|
1114
|
+
.ox-skip:focus { transform: none; }
|
|
1115
|
+
|
|
1116
|
+
/* =========================================================
|
|
1117
|
+
26. Touch screens: comfortable tap targets
|
|
1118
|
+
========================================================= */
|
|
1119
|
+
@media (pointer: coarse) {
|
|
1120
|
+
.ox-btn-sm, .ox-chip, .ox-pagination a, .ox-pagination span { min-height: 2.5rem; }
|
|
1121
|
+
.ox-btn-sm.ox-btn-icon { width: 2.5rem; }
|
|
1122
|
+
.ox-segmented button { height: 2.25rem; }
|
|
1123
|
+
.ox-tab { padding-block: var(--ox-space-3); }
|
|
1124
|
+
.ox-dropdown-item { min-height: 2.75rem; }
|
|
1125
|
+
.ox-checkbox, .ox-radio, .ox-switch { min-height: 2.25rem; }
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/* =========================================================
|
|
1129
|
+
27. Keyframes & motion preferences
|
|
871
1130
|
========================================================= */
|
|
872
1131
|
@keyframes ox-spin { to { transform: rotate(360deg); } }
|
|
873
1132
|
@keyframes ox-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
|
|
@@ -884,3 +1143,7 @@
|
|
|
884
1143
|
transition-duration: 1ms !important;
|
|
885
1144
|
}
|
|
886
1145
|
}
|
|
1146
|
+
|
|
1147
|
+
/* Theme switch: the new theme is revealed by a clip-path circle (see Onyx.theme.set) */
|
|
1148
|
+
::view-transition-old(root),
|
|
1149
|
+
::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
|
package/src/onyx.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Onyx v1.
|
|
2
|
+
* Onyx v1.1.1 — interactive behaviors
|
|
3
3
|
* Zero dependencies. Works via data attributes, or the window.Onyx API.
|
|
4
4
|
*/
|
|
5
5
|
(function (global) {
|
|
@@ -107,11 +107,25 @@
|
|
|
107
107
|
return global.matchMedia && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
function
|
|
110
|
+
function applyTheme(theme) {
|
|
111
111
|
document.documentElement.setAttribute("data-theme", theme);
|
|
112
112
|
try { localStorage.setItem(THEME_KEY, theme); } catch (e) { /* storage unavailable */ }
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// With a point ({ x, y }), the new theme spreads out from it like ink, where the browser supports it
|
|
116
|
+
function setTheme(theme, from) {
|
|
117
|
+
var reduce = global.matchMedia && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
118
|
+
if (!from || reduce || !document.startViewTransition) { applyTheme(theme); return; }
|
|
119
|
+
var r = Math.hypot(Math.max(from.x, innerWidth - from.x), Math.max(from.y, innerHeight - from.y));
|
|
120
|
+
var vt = document.startViewTransition(function () { applyTheme(theme); });
|
|
121
|
+
vt.ready.then(function () {
|
|
122
|
+
document.documentElement.animate(
|
|
123
|
+
{ clipPath: ["circle(0px at " + from.x + "px " + from.y + "px)", "circle(" + r + "px at " + from.x + "px " + from.y + "px)"] },
|
|
124
|
+
{ duration: 520, easing: "cubic-bezier(0.2, 0.8, 0.2, 1)", pseudoElement: "::view-transition-new(root)" }
|
|
125
|
+
);
|
|
126
|
+
}).catch(function () {});
|
|
127
|
+
}
|
|
128
|
+
|
|
115
129
|
function restoreTheme() {
|
|
116
130
|
try {
|
|
117
131
|
var saved = localStorage.getItem(THEME_KEY);
|
|
@@ -125,10 +139,101 @@
|
|
|
125
139
|
document.querySelectorAll(".ox-navbar").forEach(function (n) { n.setAttribute("data-scrolled", scrolled); });
|
|
126
140
|
}
|
|
127
141
|
|
|
142
|
+
/* ---------- Mobile navigation menu ---------- */
|
|
143
|
+
function setNav(nav, open) {
|
|
144
|
+
if (!nav) return;
|
|
145
|
+
nav.setAttribute("data-open", String(open));
|
|
146
|
+
var toggle = nav.querySelector("[data-ox-nav-toggle]");
|
|
147
|
+
if (toggle) {
|
|
148
|
+
toggle.setAttribute("aria-expanded", String(open));
|
|
149
|
+
toggle.setAttribute("aria-label", open ? "Close menu" : "Open menu");
|
|
150
|
+
}
|
|
151
|
+
document.body.classList.toggle("ox-nav-locked", open);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* ---------- Range slider fill ---------- */
|
|
155
|
+
function syncRange(r) {
|
|
156
|
+
var min = +r.min || 0, max = r.max === "" ? 100 : +r.max;
|
|
157
|
+
r.style.setProperty("--_pct", ((+r.value - min) / (max - min || 1)) * 100 + "%");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ---------- Number stepper ---------- */
|
|
161
|
+
function step(btn) {
|
|
162
|
+
var box = btn.closest(".ox-stepper");
|
|
163
|
+
var input = box.querySelector("input");
|
|
164
|
+
var by = +btn.getAttribute("data-step") * (+input.step || 1);
|
|
165
|
+
var min = input.min === "" ? -Infinity : +input.min;
|
|
166
|
+
var max = input.max === "" ? Infinity : +input.max;
|
|
167
|
+
var next = Math.min(max, Math.max(min, (+input.value || 0) + by));
|
|
168
|
+
input.value = +next.toFixed(6);
|
|
169
|
+
syncStepper(box);
|
|
170
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
171
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
172
|
+
}
|
|
173
|
+
function syncStepper(box) {
|
|
174
|
+
var input = box.querySelector("input");
|
|
175
|
+
var v = +input.value;
|
|
176
|
+
box.querySelectorAll("[data-step]").forEach(function (b) {
|
|
177
|
+
var dir = +b.getAttribute("data-step");
|
|
178
|
+
b.disabled = dir < 0 ? input.min !== "" && v <= +input.min : input.max !== "" && v >= +input.max;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ---------- Chips ---------- */
|
|
183
|
+
function toggleChip(chip) {
|
|
184
|
+
var group = chip.closest(".ox-chips");
|
|
185
|
+
var on = chip.getAttribute("aria-pressed") !== "true";
|
|
186
|
+
if (group && group.hasAttribute("data-single")) {
|
|
187
|
+
group.querySelectorAll(".ox-chip").forEach(function (c) { c.setAttribute("aria-pressed", "false"); });
|
|
188
|
+
on = true;
|
|
189
|
+
}
|
|
190
|
+
chip.setAttribute("aria-pressed", String(on));
|
|
191
|
+
if (group) {
|
|
192
|
+
var values = Array.prototype.filter.call(group.querySelectorAll(".ox-chip"), function (c) { return c.getAttribute("aria-pressed") === "true"; })
|
|
193
|
+
.map(function (c) { return c.value || c.textContent.trim(); });
|
|
194
|
+
group.dispatchEvent(new CustomEvent("ox:change", { bubbles: true, detail: { value: values } }));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function initWidgets(root) {
|
|
199
|
+
(root || document).querySelectorAll(".ox-range").forEach(syncRange);
|
|
200
|
+
(root || document).querySelectorAll(".ox-stepper").forEach(syncStepper);
|
|
201
|
+
// Banners dismissed before stay hidden
|
|
202
|
+
(root || document).querySelectorAll(".ox-banner[data-ox-banner]").forEach(function (b) {
|
|
203
|
+
try { if (localStorage.getItem("ox-banner-" + b.getAttribute("data-ox-banner"))) b.hidden = true; } catch (e) { /* storage unavailable */ }
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
128
207
|
/* ---------- Event delegation ---------- */
|
|
208
|
+
document.addEventListener("input", function (e) {
|
|
209
|
+
if (e.target.classList && e.target.classList.contains("ox-range")) syncRange(e.target);
|
|
210
|
+
var box = e.target.closest && e.target.closest(".ox-stepper");
|
|
211
|
+
if (box) syncStepper(box);
|
|
212
|
+
});
|
|
213
|
+
|
|
129
214
|
document.addEventListener("click", function (e) {
|
|
130
215
|
var t = e.target;
|
|
131
216
|
|
|
217
|
+
var navToggle = t.closest("[data-ox-nav-toggle]");
|
|
218
|
+
if (navToggle) { var nav = navToggle.closest(".ox-navbar"); setNav(nav, nav.getAttribute("data-open") !== "true"); return; }
|
|
219
|
+
if (t.closest(".ox-navbar-menu a")) setNav(t.closest(".ox-navbar"), false);
|
|
220
|
+
|
|
221
|
+
var stepBtn = t.closest(".ox-stepper [data-step]");
|
|
222
|
+
if (stepBtn) { step(stepBtn); return; }
|
|
223
|
+
|
|
224
|
+
var chip = t.closest(".ox-chip");
|
|
225
|
+
if (chip) { toggleChip(chip); return; }
|
|
226
|
+
|
|
227
|
+
var dismiss = t.closest("[data-ox-dismiss]");
|
|
228
|
+
if (dismiss) {
|
|
229
|
+
var banner = dismiss.closest(".ox-banner");
|
|
230
|
+
if (banner) {
|
|
231
|
+
banner.hidden = true;
|
|
232
|
+
try { if (banner.getAttribute("data-ox-banner")) localStorage.setItem("ox-banner-" + banner.getAttribute("data-ox-banner"), "1"); } catch (err) { /* storage unavailable */ }
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
132
237
|
var opener = t.closest("[data-ox-modal-open]");
|
|
133
238
|
if (opener) { openModal(opener.getAttribute("data-ox-modal-open")); return; }
|
|
134
239
|
|
|
@@ -160,7 +265,11 @@
|
|
|
160
265
|
var toaster = t.closest("[data-ox-toast]");
|
|
161
266
|
if (toaster) toast(toaster.getAttribute("data-ox-toast"));
|
|
162
267
|
|
|
163
|
-
|
|
268
|
+
var themeBtn = t.closest("[data-ox-theme-toggle]");
|
|
269
|
+
if (themeBtn) {
|
|
270
|
+
var r = themeBtn.getBoundingClientRect();
|
|
271
|
+
setTheme(currentTheme() === "dark" ? "light" : "dark", { x: r.left + r.width / 2, y: r.top + r.height / 2 });
|
|
272
|
+
}
|
|
164
273
|
});
|
|
165
274
|
|
|
166
275
|
document.addEventListener("keydown", function (e) {
|
|
@@ -168,6 +277,8 @@
|
|
|
168
277
|
if (e.key === "Escape") {
|
|
169
278
|
if (openModalEl) closeModal(openModalEl);
|
|
170
279
|
closeAllDropdowns();
|
|
280
|
+
var openNav = document.querySelector('.ox-navbar[data-open="true"]');
|
|
281
|
+
if (openNav) { setNav(openNav, false); var tg = openNav.querySelector("[data-ox-nav-toggle]"); if (tg) tg.focus(); }
|
|
171
282
|
}
|
|
172
283
|
if (e.key === "Tab" && openModalEl) trapFocus(openModalEl, e);
|
|
173
284
|
|
|
@@ -183,14 +294,24 @@
|
|
|
183
294
|
});
|
|
184
295
|
|
|
185
296
|
global.addEventListener("scroll", syncNavbars, { passive: true });
|
|
186
|
-
|
|
297
|
+
// Close the mobile menu if the window grows past the breakpoint while it's open
|
|
298
|
+
global.addEventListener("resize", function () {
|
|
299
|
+
document.querySelectorAll('.ox-navbar[data-open="true"]').forEach(function (n) {
|
|
300
|
+
var toggle = n.querySelector("[data-ox-nav-toggle]");
|
|
301
|
+
if (toggle && getComputedStyle(toggle).display === "none") setNav(n, false);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
document.addEventListener("DOMContentLoaded", function () { syncNavbars(); initWidgets(); });
|
|
305
|
+
if (document.readyState !== "loading") initWidgets();
|
|
187
306
|
restoreTheme();
|
|
188
307
|
|
|
189
308
|
global.Onyx = {
|
|
190
|
-
version: "1.
|
|
309
|
+
version: "1.1.1",
|
|
191
310
|
modal: { open: openModal, close: closeModal },
|
|
192
311
|
toast: toast,
|
|
193
312
|
copy: copy,
|
|
194
|
-
theme: { get: currentTheme, set: setTheme }
|
|
313
|
+
theme: { get: currentTheme, set: setTheme },
|
|
314
|
+
nav: { open: function (n) { setNav(n || document.querySelector(".ox-navbar"), true); }, close: function (n) { setNav(n || document.querySelector(".ox-navbar"), false); } },
|
|
315
|
+
init: initWidgets
|
|
195
316
|
};
|
|
196
317
|
})(window);
|