nexus-shared 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +446 -262
  2. package/LICENSE +21 -21
  3. package/README.md +1084 -1000
  4. package/dist/Client.Index.d.ts +3 -0
  5. package/dist/Client.Index.js +3 -0
  6. package/dist/Components/Chats/ChatParts.d.ts +2 -2
  7. package/dist/Components/Chats/ChatParts.js +3 -7
  8. package/dist/Components/Chats/ChatThread.js +3 -3
  9. package/dist/Components/Documents/Button.d.ts +6 -1
  10. package/dist/Components/Documents/Button.js +2 -2
  11. package/dist/Components/Documents/Menu.js +21 -9
  12. package/dist/Components/Documents/SplitButton.d.ts +3 -1
  13. package/dist/Components/Documents/SplitButton.js +7 -5
  14. package/dist/Components/Documents/TabButtons.d.ts +5 -2
  15. package/dist/Components/Documents/TabButtons.js +2 -2
  16. package/dist/Components/Forms/ApiForm.d.ts +1 -1
  17. package/dist/Components/Forms/ApiForm.js +1 -1
  18. package/dist/Components/Forms/Crud.js +177 -58
  19. package/dist/Components/Forms/CrudParts.d.ts +5 -1
  20. package/dist/Components/Forms/CrudParts.js +37 -5
  21. package/dist/Components/Forms/ExcelImport.d.ts +23 -7
  22. package/dist/Components/Forms/ExcelImport.js +189 -60
  23. package/dist/Components/Forms/Form.js +5 -13
  24. package/dist/Components/Forms/SubmitForm.d.ts +11 -2
  25. package/dist/Components/Forms/SubmitForm.js +23 -12
  26. package/dist/Components/Inputs/DropdownParts.d.ts +1 -1
  27. package/dist/Components/Inputs/DropdownParts.js +4 -6
  28. package/dist/Components/Inputs/NumberBox.js +24 -2
  29. package/dist/Components/Inputs/RowsInput.js +2 -2
  30. package/dist/Components/Inputs/Slider.js +4 -1
  31. package/dist/Components/Inputs/TabularForm.js +42 -10
  32. package/dist/Components/Inputs/TimePanel.js +4 -3
  33. package/dist/Components/Layouts/Popup.js +1 -4
  34. package/dist/Components/Layouts/ThemePicker.d.ts +20 -3
  35. package/dist/Components/Layouts/ThemePicker.js +77 -6
  36. package/dist/Components/Layouts/ThemeScript.d.ts +4 -2
  37. package/dist/Components/Layouts/ThemeScript.js +4 -2
  38. package/dist/Components/Layouts/ThemeSwitcher.d.ts +6 -3
  39. package/dist/Components/Layouts/ThemeSwitcher.js +9 -8
  40. package/dist/Components/Viewers/Avatar.d.ts +21 -0
  41. package/dist/Components/Viewers/Avatar.js +21 -0
  42. package/dist/Components/Viewers/DataTable.js +192 -44
  43. package/dist/Components/Viewers/DataTableColumns.d.ts +7 -3
  44. package/dist/Components/Viewers/DataTableColumns.js +25 -68
  45. package/dist/Components/Viewers/DataTableFilters.d.ts +21 -0
  46. package/dist/Components/Viewers/DataTableFilters.js +68 -0
  47. package/dist/Components/Viewers/DataTableParts.d.ts +2 -2
  48. package/dist/Components/Viewers/DataTableParts.js +3 -3
  49. package/dist/Helpers/AnimationHelpers.d.ts +11 -0
  50. package/dist/Helpers/AnimationHelpers.js +20 -2
  51. package/dist/Helpers/CrudBackend.d.ts +7 -4
  52. package/dist/Helpers/CrudBackend.js +4 -2
  53. package/dist/Helpers/ExcelBackend.d.ts +3 -3
  54. package/dist/Helpers/ExcelBackend.js +8 -2
  55. package/dist/Helpers/ExcelHelpers.d.ts +36 -8
  56. package/dist/Helpers/ExcelHelpers.js +55 -13
  57. package/dist/Helpers/FormStore.d.ts +9 -5
  58. package/dist/Helpers/FormStore.js +25 -5
  59. package/dist/Helpers/MessageBuilder.js +1 -0
  60. package/dist/Helpers/NumberHelpers.d.ts +7 -1
  61. package/dist/Helpers/NumberHelpers.js +41 -3
  62. package/dist/Helpers/PopoverHelpers.d.ts +17 -5
  63. package/dist/Helpers/PopoverHelpers.js +52 -17
  64. package/dist/Helpers/PopoverHooks.d.ts +24 -0
  65. package/dist/Helpers/PopoverHooks.js +62 -0
  66. package/dist/Helpers/RowsStore.d.ts +7 -2
  67. package/dist/Helpers/RowsStore.js +15 -3
  68. package/dist/Helpers/TableExport.js +12 -12
  69. package/dist/Helpers/TableFilters.d.ts +25 -0
  70. package/dist/Helpers/TableFilters.js +162 -0
  71. package/dist/Helpers/TableHelpers.d.ts +6 -3
  72. package/dist/Helpers/TableHelpers.js +8 -4
  73. package/dist/Helpers/TableStore.d.ts +11 -3
  74. package/dist/Helpers/TableStore.js +16 -5
  75. package/dist/Helpers/ThemeHelpers.d.ts +21 -4
  76. package/dist/Helpers/ThemeHelpers.js +46 -9
  77. package/dist/Helpers/UserPreferences.d.ts +14 -0
  78. package/dist/Helpers/UserPreferences.js +99 -0
  79. package/dist/Interfaces/CrudInterfaces.d.ts +20 -1
  80. package/dist/Interfaces/ExcelInterfaces.d.ts +12 -0
  81. package/dist/Interfaces/FormInterfaces.d.ts +33 -5
  82. package/dist/Interfaces/PreferenceInterfaces.d.ts +43 -0
  83. package/dist/Interfaces/PreferenceInterfaces.js +28 -0
  84. package/dist/Interfaces/TableInterfaces.d.ts +59 -12
  85. package/dist/Interfaces/ThemeInterfaces.d.ts +25 -0
  86. package/dist/Interfaces/ThemeInterfaces.js +15 -0
  87. package/dist/Services/ExcelApi.d.ts +3 -1
  88. package/dist/Services/ExcelApi.js +1 -1
  89. package/dist/Services/ThemeService.d.ts +18 -2
  90. package/dist/Services/ThemeService.js +80 -16
  91. package/dist/Shared.Index.d.ts +2 -0
  92. package/dist/Shared.Index.js +2 -0
  93. package/package.json +85 -85
  94. package/src/Styles/Nexus.Avatar.css +79 -0
  95. package/src/Styles/Nexus.Base.css +117 -70
  96. package/src/Styles/Nexus.Button.css +180 -153
  97. package/src/Styles/Nexus.Calendar.css +273 -275
  98. package/src/Styles/Nexus.Chat.css +1443 -1489
  99. package/src/Styles/Nexus.Choice.css +343 -345
  100. package/src/Styles/Nexus.Crud.css +168 -161
  101. package/src/Styles/Nexus.DateTime.css +81 -81
  102. package/src/Styles/Nexus.Dropdown.css +725 -774
  103. package/src/Styles/Nexus.Editor.css +295 -297
  104. package/src/Styles/Nexus.Excel.css +389 -370
  105. package/src/Styles/Nexus.Form.css +279 -321
  106. package/src/Styles/Nexus.Index.css +37 -36
  107. package/src/Styles/Nexus.Input.css +602 -637
  108. package/src/Styles/Nexus.Markdown.css +237 -265
  109. package/src/Styles/Nexus.Menu.css +237 -259
  110. package/src/Styles/Nexus.Popup.css +724 -731
  111. package/src/Styles/Nexus.RichText.css +2 -32
  112. package/src/Styles/Nexus.Rows.css +637 -596
  113. package/src/Styles/Nexus.Slider.css +276 -278
  114. package/src/Styles/Nexus.Tab.Buttons.css +432 -419
  115. package/src/Styles/Nexus.Table.css +1386 -1296
  116. package/src/Styles/Nexus.Theme.Picker.css +286 -232
  117. package/src/Styles/Nexus.Themes.css +194 -194
  118. package/src/Styles/Nexus.Time.css +144 -147
  119. package/src/Styles/Nexus.Toast.css +263 -265
  120. package/src/Styles/Nexus.Tokens.css +89 -72
  121. package/src/Styles/Nexus.Tree.css +431 -435
@@ -0,0 +1,79 @@
1
+ /*
2
+ * A person's avatar, wherever one is drawn: the chat, a dropdown's avatar column, the admin bar.
3
+ */
4
+ @layer nexus.components {
5
+ .nx-avatar {
6
+ position: relative;
7
+ display: inline-grid;
8
+ flex-shrink: 0;
9
+ place-items: center;
10
+ width: var(--nx-avatar-size, 28px);
11
+ height: var(--nx-avatar-size, 28px);
12
+ padding: 0;
13
+ overflow: hidden;
14
+ border: 0;
15
+ border-radius: 50%;
16
+ background: color-mix(in oklab, var(--nx-text) 10%, var(--nx-surface));
17
+ color: var(--nx-text);
18
+ font: inherit;
19
+ font-size: max(8px, calc(var(--nx-avatar-size, 28px) * 0.4));
20
+ font-weight: 600;
21
+ line-height: 1;
22
+ }
23
+
24
+ .nx-avatar--sm {
25
+ --nx-avatar-size: 22px;
26
+ }
27
+
28
+ .nx-avatar--lg {
29
+ --nx-avatar-size: 44px;
30
+ }
31
+
32
+ .nx-avatar[data-tone="primary"] {
33
+ background: var(--nx-primary-soft);
34
+ }
35
+
36
+ .nx-avatar[data-tone="info"] {
37
+ background: var(--nx-info-soft);
38
+ }
39
+
40
+ .nx-avatar[data-tone="success"] {
41
+ background: var(--nx-success-soft);
42
+ }
43
+
44
+ .nx-avatar[data-tone="warning"] {
45
+ background: var(--nx-warning-soft);
46
+ }
47
+
48
+ .nx-avatar img {
49
+ position: absolute;
50
+ inset: 0;
51
+ width: 100%;
52
+ height: 100%;
53
+ object-fit: cover;
54
+ }
55
+
56
+ /* A picture that can be changed: hovering or focusing it lays a camera over it. Opacity alone, no animation. */
57
+ button.nx-avatar {
58
+ cursor: pointer;
59
+ }
60
+
61
+ button.nx-avatar:focus-visible {
62
+ outline: none;
63
+ box-shadow: var(--nx-focus-ring);
64
+ }
65
+
66
+ .nx-avatar__veil {
67
+ position: absolute;
68
+ inset: 0;
69
+ display: grid;
70
+ place-items: center;
71
+ background: rgb(16 24 40 / 0.55);
72
+ color: #fff;
73
+ opacity: 0;
74
+ }
75
+
76
+ button.nx-avatar:is(:hover, :focus-visible) .nx-avatar__veil {
77
+ opacity: 1;
78
+ }
79
+ }
@@ -1,70 +1,117 @@
1
- /* Page defaults and small shared utilities. */
2
- @layer nexus.base {
3
- *,
4
- *::before,
5
- *::after {
6
- box-sizing: border-box;
7
- }
8
-
9
- body {
10
- margin: 0;
11
- background: var(--nx-bg);
12
- color: var(--nx-text);
13
- font: var(--nx-text-md) / var(--nx-leading) var(--nx-font);
14
- -webkit-font-smoothing: antialiased;
15
- text-rendering: optimizeLegibility;
16
- }
17
-
18
- button,
19
- input,
20
- select,
21
- textarea {
22
- font: inherit;
23
- color: inherit;
24
- }
25
-
26
- /* Nepali text takes a font that has Devanagari glyphs, digits included. */
27
- :lang(ne) {
28
- font-family: var(--nx-font-devanagari);
29
- }
30
-
31
- :focus-visible {
32
- outline: 2px solid var(--nx-primary);
33
- outline-offset: 2px;
34
- }
35
-
36
- /* Hidden on screen but still read by screen readers and reachable with the keyboard. */
37
- .nx-visually-hidden {
38
- position: absolute;
39
- width: 1px;
40
- height: 1px;
41
- margin: -1px;
42
- padding: 0;
43
- overflow: hidden;
44
- clip-path: inset(50%);
45
- white-space: nowrap;
46
- border: 0;
47
- }
48
-
49
- /* Icons sit on the text baseline and never shrink inside flex rows. */
50
- .nx-icon {
51
- flex-shrink: 0;
52
- vertical-align: -0.125em;
53
- }
54
-
55
- .nx-spin {
56
- animation: nx-spin 0.8s linear infinite;
57
- }
58
-
59
- @keyframes nx-spin {
60
- to {
61
- transform: rotate(360deg);
62
- }
63
- }
64
-
65
- @media (prefers-reduced-motion: reduce) {
66
- .nx-spin {
67
- animation-duration: 2.4s;
68
- }
69
- }
70
- }
1
+ /* Page defaults and small shared utilities. */
2
+ @layer nexus.base {
3
+ *,
4
+ *::before,
5
+ *::after {
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ body {
10
+ margin: 0;
11
+ background: var(--nx-bg);
12
+ color: var(--nx-text);
13
+ font: var(--nx-text-md) / var(--nx-leading) var(--nx-font);
14
+ -webkit-font-smoothing: antialiased;
15
+ text-rendering: optimizeLegibility;
16
+ }
17
+
18
+ button,
19
+ input,
20
+ select,
21
+ textarea {
22
+ font: inherit;
23
+ color: inherit;
24
+ }
25
+
26
+ /* Nepali text takes a font that has Devanagari glyphs, digits included. */
27
+ :lang(ne) {
28
+ font-family: var(--nx-font-devanagari);
29
+ }
30
+
31
+ :focus-visible {
32
+ outline: 2px solid var(--nx-primary);
33
+ outline-offset: 2px;
34
+ }
35
+
36
+ /*
37
+ * One scroll bar for every scroller, the design lab's: thin and quiet, a thumb of the text colour at 22% on no
38
+ * track. On Windows, Chrome and Edge draw it 10px wide with small arrows and darken the thumb under the pointer
39
+ * themselves, and Firefox's thin bar is 8px; Safari, which has no `scrollbar-color` yet, draws its own thin bar.
40
+ * `scrollbar-width` is not inherited, so it is set on every element, and a gutter kept with `scrollbar-gutter` is as
41
+ * wide as the bar it waits for. There is no `::-webkit-scrollbar` here because Chrome, Edge and Safari ignore it on an
42
+ * element that sets `scrollbar-width`. A component that needs another look sets the two properties on its own
43
+ * scroller. With forced colours the browser puts the system's colours back.
44
+ */
45
+ * {
46
+ scrollbar-width: thin;
47
+ scrollbar-color: color-mix(in oklab, var(--nx-text) 22%, transparent) transparent;
48
+ }
49
+
50
+ /* Hidden on screen but still read by screen readers and reachable with the keyboard. */
51
+ .nx-visually-hidden {
52
+ position: absolute;
53
+ width: 1px;
54
+ height: 1px;
55
+ margin: -1px;
56
+ padding: 0;
57
+ overflow: hidden;
58
+ clip-path: inset(50%);
59
+ white-space: nowrap;
60
+ border: 0;
61
+ }
62
+
63
+ /* Icons sit on the text baseline and never shrink inside flex rows. */
64
+ .nx-icon {
65
+ flex-shrink: 0;
66
+ vertical-align: -0.125em;
67
+ }
68
+
69
+ .nx-spin {
70
+ animation: nx-spin 0.8s linear infinite;
71
+ }
72
+
73
+ @keyframes nx-spin {
74
+ to {
75
+ transform: rotate(360deg);
76
+ }
77
+ }
78
+
79
+ /* A spinner says something is still happening, so with animations off it turns slowly rather than stopping. */
80
+ [data-nx-motion="reduce"] .nx-spin {
81
+ animation-duration: 2.4s;
82
+ }
83
+
84
+ /*
85
+ * The scale grid, a development tool from the theme panel: a ruled sheet over the whole window, so an edge one pixel
86
+ * out is seen rather than argued about. `px` rules every 4px with a solid line every 48px; `cm` rules millimetres
87
+ * with the centimetre solid. Lines start on the window's edge, so every reading counts from there. It takes no
88
+ * clicks and moves nothing; popups and menus sit in the top layer, above it. ThemeScript sets it in a development
89
+ * build only. The lines mix the text colour, so they read on all seven themes in both modes.
90
+ */
91
+ [data-nx-grid] body::after {
92
+ --nx-grid-minor: color-mix(in oklab, var(--nx-text) 14%, transparent);
93
+ --nx-grid-major: color-mix(in oklab, var(--nx-text) 34%, transparent);
94
+
95
+ position: fixed;
96
+ inset: 0;
97
+ z-index: 2147483000;
98
+ pointer-events: none;
99
+ content: "";
100
+ }
101
+
102
+ [data-nx-grid="px"] body::after {
103
+ background-image:
104
+ repeating-linear-gradient(to right, var(--nx-grid-major) 0 1px, transparent 1px 48px),
105
+ repeating-linear-gradient(to bottom, var(--nx-grid-major) 0 1px, transparent 1px 48px),
106
+ repeating-linear-gradient(to right, var(--nx-grid-minor) 0 1px, transparent 1px 4px),
107
+ repeating-linear-gradient(to bottom, var(--nx-grid-minor) 0 1px, transparent 1px 4px);
108
+ }
109
+
110
+ [data-nx-grid="cm"] body::after {
111
+ background-image:
112
+ repeating-linear-gradient(to right, var(--nx-grid-major) 0 1px, transparent 1px 1cm),
113
+ repeating-linear-gradient(to bottom, var(--nx-grid-major) 0 1px, transparent 1px 1cm),
114
+ repeating-linear-gradient(to right, var(--nx-grid-minor) 0 1px, transparent 1px 1mm),
115
+ repeating-linear-gradient(to bottom, var(--nx-grid-minor) 0 1px, transparent 1px 1mm);
116
+ }
117
+ }
@@ -1,153 +1,180 @@
1
- @layer nexus.components {
2
- .nx-btn {
3
- --nx-btn-height: var(--nx-control-md);
4
- --nx-btn-bg: var(--nx-surface);
5
- --nx-btn-bg-hover: var(--nx-surface-hover);
6
- --nx-btn-color: var(--nx-text);
7
- --nx-btn-border: var(--nx-border-strong);
8
-
9
- display: inline-flex;
10
- align-items: center;
11
- justify-content: center;
12
- gap: var(--nx-space-2);
13
- height: var(--nx-btn-height);
14
- padding: 0 var(--nx-space-3);
15
- border: 1px solid var(--nx-btn-border);
16
- border-radius: var(--nx-radius);
17
- background: var(--nx-btn-bg);
18
- color: var(--nx-btn-color);
19
- font-size: var(--nx-text-md);
20
- font-weight: 500;
21
- line-height: 1;
22
- white-space: nowrap;
23
- /* The same classes make a link look like a button, for a call to action that navigates. */
24
- text-decoration: none;
25
- cursor: pointer;
26
- user-select: none;
27
- box-shadow: var(--nx-shadow-sm);
28
- transition:
29
- background-color var(--nx-duration),
30
- border-color var(--nx-duration),
31
- color var(--nx-duration),
32
- box-shadow var(--nx-duration),
33
- transform var(--nx-duration);
34
- }
35
-
36
- .nx-btn .nx-icon {
37
- width: 1.15em;
38
- height: 1.15em;
39
- vertical-align: 0;
40
- }
41
-
42
- .nx-btn:hover:not(:disabled) {
43
- background: var(--nx-btn-bg-hover);
44
- }
45
-
46
- .nx-btn:active:not(:disabled) {
47
- transform: translateY(1px);
48
- }
49
-
50
- .nx-btn:focus-visible {
51
- outline: none;
52
- box-shadow: var(--nx-focus-ring);
53
- }
54
-
55
- .nx-btn:disabled {
56
- cursor: not-allowed;
57
- opacity: 0.55;
58
- box-shadow: none;
59
- }
60
-
61
- .nx-btn[aria-busy="true"] {
62
- cursor: progress;
63
- opacity: 0.8;
64
- }
65
-
66
- /* Variants */
67
- .nx-btn--primary {
68
- --nx-btn-bg: var(--nx-primary-solid);
69
- --nx-btn-bg-hover: var(--nx-primary-hover);
70
- --nx-btn-color: var(--nx-primary-text);
71
- --nx-btn-border: transparent;
72
- }
73
-
74
- .nx-btn--danger {
75
- --nx-btn-bg: var(--nx-danger-solid);
76
- --nx-btn-bg-hover: var(--nx-danger-hover);
77
- --nx-btn-color: var(--nx-danger-text);
78
- --nx-btn-border: transparent;
79
- }
80
-
81
- .nx-btn--ghost {
82
- --nx-btn-bg: transparent;
83
- --nx-btn-border: transparent;
84
- box-shadow: none;
85
- }
86
-
87
- /*
88
- * Hover tones: a secondary or ghost button stays neutral, then takes the tone on its border,
89
- * a soft background, and its text while hovered or focused from the keyboard.
90
- * Every tone meets WCAG AA contrast on its soft background (npm run check:themes).
91
- */
92
- .nx-btn--hover-primary {
93
- --nx-btn-tone: var(--nx-primary);
94
- --nx-btn-tone-soft: var(--nx-primary-soft);
95
- }
96
-
97
- .nx-btn--hover-danger {
98
- --nx-btn-tone: var(--nx-danger);
99
- --nx-btn-tone-soft: var(--nx-danger-soft);
100
- }
101
-
102
- .nx-btn--hover-success {
103
- --nx-btn-tone: var(--nx-success);
104
- --nx-btn-tone-soft: var(--nx-success-soft);
105
- }
106
-
107
- .nx-btn--hover-warning {
108
- --nx-btn-tone: var(--nx-warning);
109
- --nx-btn-tone-soft: var(--nx-warning-soft);
110
- }
111
-
112
- .nx-btn--hover-info {
113
- --nx-btn-tone: var(--nx-info);
114
- --nx-btn-tone-soft: var(--nx-info-soft);
115
- }
116
-
117
- :is(.nx-btn--secondary, .nx-btn--ghost):is(
118
- .nx-btn--hover-primary,
119
- .nx-btn--hover-danger,
120
- .nx-btn--hover-success,
121
- .nx-btn--hover-warning,
122
- .nx-btn--hover-info
123
- ):is(:hover, :focus-visible):not(:disabled) {
124
- --nx-btn-bg: var(--nx-btn-tone-soft);
125
- --nx-btn-bg-hover: var(--nx-btn-tone-soft);
126
- --nx-btn-border: var(--nx-btn-tone);
127
- --nx-btn-color: var(--nx-btn-tone);
128
- }
129
-
130
- /* Sizes */
131
- .nx-btn--sm {
132
- --nx-btn-height: var(--nx-control-sm);
133
- padding: 0 var(--nx-space-2);
134
- gap: 6px;
135
- font-size: var(--nx-text-sm);
136
- }
137
-
138
- .nx-btn--lg {
139
- --nx-btn-height: var(--nx-control-lg);
140
- padding: 0 var(--nx-space-4);
141
- font-size: var(--nx-text-lg);
142
- }
143
-
144
- .nx-btn--icon {
145
- width: var(--nx-btn-height);
146
- padding: 0;
147
- }
148
-
149
- .nx-btn--block {
150
- display: flex;
151
- width: 100%;
152
- }
153
- }
1
+ @layer nexus.components {
2
+ .nx-btn {
3
+ --nx-btn-height: var(--nx-control-md);
4
+ --nx-btn-bg: var(--nx-surface);
5
+ --nx-btn-bg-hover: var(--nx-surface-hover);
6
+ --nx-btn-color: var(--nx-text);
7
+ --nx-btn-border: var(--nx-border-strong);
8
+
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ gap: var(--nx-space-2);
13
+ height: var(--nx-btn-height);
14
+ padding: 0 var(--nx-space-3);
15
+ border: 1px solid var(--nx-btn-border);
16
+ border-radius: var(--nx-radius);
17
+ background: var(--nx-btn-bg);
18
+ color: var(--nx-btn-color);
19
+ font-size: var(--nx-text-md);
20
+ font-weight: 500;
21
+ line-height: 1;
22
+ white-space: nowrap;
23
+ /* The same classes make a link look like a button, for a call to action that navigates. */
24
+ text-decoration: none;
25
+ cursor: pointer;
26
+ user-select: none;
27
+ box-shadow: var(--nx-shadow-sm);
28
+ transition:
29
+ background-color var(--nx-duration),
30
+ border-color var(--nx-duration),
31
+ color var(--nx-duration),
32
+ box-shadow var(--nx-duration),
33
+ transform var(--nx-duration);
34
+ }
35
+
36
+ .nx-btn .nx-icon {
37
+ width: 1.15em;
38
+ height: 1.15em;
39
+ vertical-align: 0;
40
+ }
41
+
42
+ .nx-btn:hover:not(:disabled) {
43
+ background: var(--nx-btn-bg-hover);
44
+ }
45
+
46
+ .nx-btn:active:not(:disabled) {
47
+ transform: translateY(1px);
48
+ }
49
+
50
+ .nx-btn:focus-visible {
51
+ outline: none;
52
+ box-shadow: var(--nx-focus-ring);
53
+ }
54
+
55
+ .nx-btn:disabled {
56
+ cursor: not-allowed;
57
+ opacity: 0.55;
58
+ box-shadow: none;
59
+ }
60
+
61
+ .nx-btn[aria-busy="true"] {
62
+ cursor: progress;
63
+ opacity: 0.8;
64
+ }
65
+
66
+ /* Variants */
67
+ .nx-btn--primary {
68
+ --nx-btn-bg: var(--nx-primary-solid);
69
+ --nx-btn-bg-hover: var(--nx-primary-hover);
70
+ --nx-btn-color: var(--nx-primary-text);
71
+ --nx-btn-border: transparent;
72
+ }
73
+
74
+ .nx-btn--danger {
75
+ --nx-btn-bg: var(--nx-danger-solid);
76
+ --nx-btn-bg-hover: var(--nx-danger-hover);
77
+ --nx-btn-color: var(--nx-danger-text);
78
+ --nx-btn-border: transparent;
79
+ }
80
+
81
+ .nx-btn--ghost {
82
+ --nx-btn-bg: transparent;
83
+ --nx-btn-border: transparent;
84
+ box-shadow: none;
85
+ }
86
+
87
+ /*
88
+ * Hover tones: a secondary or ghost button stays neutral, then takes the tone on its border,
89
+ * a soft background, and its text while hovered or focused from the keyboard.
90
+ * Every tone meets WCAG AA contrast on its soft background (npm run check:themes).
91
+ */
92
+ .nx-btn--hover-primary {
93
+ --nx-btn-tone: var(--nx-primary);
94
+ --nx-btn-tone-soft: var(--nx-primary-soft);
95
+ }
96
+
97
+ .nx-btn--hover-danger {
98
+ --nx-btn-tone: var(--nx-danger);
99
+ --nx-btn-tone-soft: var(--nx-danger-soft);
100
+ }
101
+
102
+ .nx-btn--hover-success {
103
+ --nx-btn-tone: var(--nx-success);
104
+ --nx-btn-tone-soft: var(--nx-success-soft);
105
+ }
106
+
107
+ .nx-btn--hover-warning {
108
+ --nx-btn-tone: var(--nx-warning);
109
+ --nx-btn-tone-soft: var(--nx-warning-soft);
110
+ }
111
+
112
+ .nx-btn--hover-info {
113
+ --nx-btn-tone: var(--nx-info);
114
+ --nx-btn-tone-soft: var(--nx-info-soft);
115
+ }
116
+
117
+ :is(.nx-btn--secondary, .nx-btn--ghost):is(
118
+ .nx-btn--hover-primary,
119
+ .nx-btn--hover-danger,
120
+ .nx-btn--hover-success,
121
+ .nx-btn--hover-warning,
122
+ .nx-btn--hover-info
123
+ ):is(:hover, :focus-visible):not(:disabled) {
124
+ --nx-btn-bg: var(--nx-btn-tone-soft);
125
+ --nx-btn-bg-hover: var(--nx-btn-tone-soft);
126
+ --nx-btn-border: var(--nx-btn-tone);
127
+ --nx-btn-color: var(--nx-btn-tone);
128
+ }
129
+
130
+ /* Sizes */
131
+ .nx-btn--sm {
132
+ --nx-btn-height: var(--nx-control-sm);
133
+ padding: 0 var(--nx-space-2);
134
+ gap: 6px;
135
+ font-size: var(--nx-text-sm);
136
+ }
137
+
138
+ .nx-btn--lg {
139
+ --nx-btn-height: var(--nx-control-lg);
140
+ padding: 0 var(--nx-space-4);
141
+ font-size: var(--nx-text-lg);
142
+ }
143
+
144
+ .nx-btn--icon {
145
+ width: var(--nx-btn-height);
146
+ padding: 0;
147
+ }
148
+
149
+ .nx-btn--block {
150
+ display: flex;
151
+ width: 100%;
152
+ }
153
+
154
+ /* Something new behind the button (red), or a state that is on (primary): a dot on its corner, ringed in the
155
+ surface so it reads over the icon. */
156
+ .nx-btn:has(> .nx-btn__dot) {
157
+ position: relative;
158
+ }
159
+
160
+ .nx-btn__dot {
161
+ position: absolute;
162
+ top: 6px;
163
+ right: 6px;
164
+ width: 7px;
165
+ height: 7px;
166
+ border: 1.5px solid var(--nx-surface);
167
+ border-radius: 50%;
168
+ background: var(--nx-danger-solid);
169
+ }
170
+
171
+ .nx-btn__dot[data-tone="primary"] {
172
+ background: var(--nx-primary-solid);
173
+ }
174
+
175
+ /* A small icon sits nearer the corner: the dot moves into it, clear of a narrow icon such as ⋮. */
176
+ .nx-btn--icon.nx-btn--sm > .nx-btn__dot {
177
+ top: 3px;
178
+ right: 3px;
179
+ }
180
+ }