nitro_kit 2.0.0.alpha.2 → 2.0.0.alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/README.md +23 -4
  4. data/STYLE_GUIDE.md +37 -5
  5. data/app/assets/stylesheets/nitro_kit.css +1714 -1358
  6. data/app/components/nitro_kit/accordion.rb +1 -7
  7. data/app/components/nitro_kit/app_navigation.rb +1 -2
  8. data/app/components/nitro_kit/app_shell.rb +1 -7
  9. data/app/components/nitro_kit/appearance_picker.rb +1 -11
  10. data/app/components/nitro_kit/avatar.rb +6 -1
  11. data/app/components/nitro_kit/avatar_stack.rb +3 -0
  12. data/app/components/nitro_kit/button_group.rb +2 -2
  13. data/app/components/nitro_kit/checkbox.rb +7 -4
  14. data/app/components/nitro_kit/checkbox_group.rb +0 -25
  15. data/app/components/nitro_kit/combobox.rb +3 -16
  16. data/app/components/nitro_kit/command_palette.rb +41 -59
  17. data/app/components/nitro_kit/component.rb +57 -5
  18. data/app/components/nitro_kit/danger_zone.rb +5 -1
  19. data/app/components/nitro_kit/data_section.rb +28 -4
  20. data/app/components/nitro_kit/details_table.rb +19 -8
  21. data/app/components/nitro_kit/dialog.rb +23 -19
  22. data/app/components/nitro_kit/dropdown.rb +1 -6
  23. data/app/components/nitro_kit/dropzone.rb +3 -14
  24. data/app/components/nitro_kit/empty_state.rb +21 -2
  25. data/app/components/nitro_kit/field.rb +35 -11
  26. data/app/components/nitro_kit/form_builder.rb +0 -1
  27. data/app/components/nitro_kit/progressive_image.rb +2 -2
  28. data/app/components/nitro_kit/radio_button.rb +7 -4
  29. data/app/components/nitro_kit/radio_button_group.rb +0 -25
  30. data/app/components/nitro_kit/select.rb +3 -3
  31. data/app/components/nitro_kit/settings_layout.rb +17 -4
  32. data/app/components/nitro_kit/{form_section.rb → settings_section.rb} +18 -11
  33. data/app/components/nitro_kit/sheet.rb +29 -20
  34. data/app/components/nitro_kit/stat_grid.rb +18 -2
  35. data/app/components/nitro_kit/table.rb +22 -3
  36. data/app/components/nitro_kit/tabs.rb +1 -13
  37. data/app/components/nitro_kit/toast.rb +4 -2
  38. data/app/components/nitro_kit/toolbar.rb +2 -0
  39. data/app/components/nitro_kit/tooltip.rb +7 -10
  40. data/app/javascript/controllers/nk/app_shell_controller.js +3 -3
  41. data/app/javascript/controllers/nk/appearance_controller.js +6 -0
  42. data/app/javascript/controllers/nk/combobox_controller.js +51 -1
  43. data/app/javascript/controllers/nk/command_palette_controller.js +21 -11
  44. data/app/javascript/controllers/nk/dialog_controller.js +54 -0
  45. data/app/javascript/controllers/nk/dropdown_controller.js +45 -0
  46. data/app/javascript/controllers/nk/dropzone_controller.js +2 -2
  47. data/app/javascript/controllers/nk/progressive_image_controller.js +11 -7
  48. data/app/javascript/controllers/nk/tabs_controller.js +8 -1
  49. data/app/javascript/controllers/nk/tooltip_controller.js +9 -0
  50. data/config/locales/en.yml +4 -1
  51. data/docs/agent_guide.md +31 -16
  52. data/docs/agent_native_spec.md +27 -10
  53. data/docs/browser_support.md +193 -0
  54. data/docs/component_contracts.md +88 -82
  55. data/docs/customization.md +27 -15
  56. data/docs/hotwire.md +15 -3
  57. data/docs/initialization_prompt.md +31 -6
  58. data/docs/migration_1_to_2.md +61 -19
  59. data/docs/patterns/application_foundation.md +6 -4
  60. data/docs/patterns/crud_resource.md +6 -3
  61. data/docs/patterns/destructive_action.md +25 -0
  62. data/docs/patterns/resource_form.md +1 -1
  63. data/docs/rails_conventions.md +4 -1
  64. data/docs/rails_integration.md +39 -11
  65. data/lib/generators/nitro_kit/upgrade_smoke_tests_generator.rb +12 -0
  66. data/lib/nitro_kit/installation.rb +9 -4
  67. data/lib/nitro_kit/migration_inventory.rb +268 -8
  68. data/lib/nitro_kit/upgrade_smoke_test.rb +10 -12
  69. data/lib/nitro_kit/version.rb +1 -1
  70. data/lib/rails/commands/nitro_kit/nitro_kit_command.rb +10 -3
  71. data/plugins/nitro-kit/skills/nitro-kit-hotwire/SKILL.md +4 -0
  72. data/plugins/nitro-kit/skills/nitro-kit-rails/SKILL.md +6 -1
  73. data/plugins/nitro-kit/skills/nitro-kit-ui/SKILL.md +18 -8
  74. data/src/stylesheets/nitro_kit/components/accordion.css +40 -40
  75. data/src/stylesheets/nitro_kit/components/alert.css +9 -9
  76. data/src/stylesheets/nitro_kit/components/app_navigation.css +19 -19
  77. data/src/stylesheets/nitro_kit/components/app_shell.css +196 -193
  78. data/src/stylesheets/nitro_kit/components/appearance_picker.css +41 -31
  79. data/src/stylesheets/nitro_kit/components/avatar.css +3 -3
  80. data/src/stylesheets/nitro_kit/components/avatar_stack.css +36 -19
  81. data/src/stylesheets/nitro_kit/components/button.css +83 -58
  82. data/src/stylesheets/nitro_kit/components/button_group.css +19 -14
  83. data/src/stylesheets/nitro_kit/components/card.css +11 -5
  84. data/src/stylesheets/nitro_kit/components/checkbox.css +40 -36
  85. data/src/stylesheets/nitro_kit/components/checkbox_group.css +13 -13
  86. data/src/stylesheets/nitro_kit/components/combobox.css +33 -23
  87. data/src/stylesheets/nitro_kit/components/command_palette.css +48 -47
  88. data/src/stylesheets/nitro_kit/components/control_group.css +52 -58
  89. data/src/stylesheets/nitro_kit/components/danger_zone.css +3 -3
  90. data/src/stylesheets/nitro_kit/components/data_section.css +1 -1
  91. data/src/stylesheets/nitro_kit/components/details_table.css +13 -13
  92. data/src/stylesheets/nitro_kit/components/dialog.css +6 -3
  93. data/src/stylesheets/nitro_kit/components/dropdown.css +29 -23
  94. data/src/stylesheets/nitro_kit/components/dropzone.css +71 -28
  95. data/src/stylesheets/nitro_kit/components/empty_state.css +6 -6
  96. data/src/stylesheets/nitro_kit/components/field.css +30 -26
  97. data/src/stylesheets/nitro_kit/components/field_group.css +13 -0
  98. data/src/stylesheets/nitro_kit/components/fieldset.css +6 -0
  99. data/src/stylesheets/nitro_kit/components/input.css +9 -2
  100. data/src/stylesheets/nitro_kit/components/layout.css +165 -165
  101. data/src/stylesheets/nitro_kit/components/page_header.css +3 -3
  102. data/src/stylesheets/nitro_kit/components/pagination.css +55 -46
  103. data/src/stylesheets/nitro_kit/components/pagination_bar.css +7 -7
  104. data/src/stylesheets/nitro_kit/components/palette.css +20 -20
  105. data/src/stylesheets/nitro_kit/components/progressive_image.css +44 -54
  106. data/src/stylesheets/nitro_kit/components/radio_button.css +36 -29
  107. data/src/stylesheets/nitro_kit/components/radio_button_group.css +43 -43
  108. data/src/stylesheets/nitro_kit/components/rich_text_area.css +15 -0
  109. data/src/stylesheets/nitro_kit/components/select.css +2 -9
  110. data/src/stylesheets/nitro_kit/components/settings_layout.css +21 -18
  111. data/src/stylesheets/nitro_kit/components/settings_section.css +84 -0
  112. data/src/stylesheets/nitro_kit/components/sheet.css +23 -3
  113. data/src/stylesheets/nitro_kit/components/stat_grid.css +10 -9
  114. data/src/stylesheets/nitro_kit/components/switch.css +37 -33
  115. data/src/stylesheets/nitro_kit/components/table.css +35 -31
  116. data/src/stylesheets/nitro_kit/components/tabs.css +43 -39
  117. data/src/stylesheets/nitro_kit/components/toast.css +20 -9
  118. data/src/stylesheets/nitro_kit/components/toolbar.css +14 -39
  119. data/src/stylesheets/nitro_kit/components/tooltip.css +34 -29
  120. data/src/stylesheets/nitro_kit/components/typeset.css +147 -1
  121. data/src/stylesheets/nitro_kit/reset.css +9 -9
  122. data/src/stylesheets/nitro_kit/tokens.css +8 -9
  123. metadata +4 -3
  124. data/src/stylesheets/nitro_kit/components/form_section.css +0 -71
@@ -7,16 +7,16 @@
7
7
  }
8
8
 
9
9
  :where(
10
- [data-nk="appearance-picker"] > [data-slot="appearance-picker-legend"]
11
- ) {
10
+ [data-nk="appearance-picker"] > [data-slot="appearance-picker-legend"]
11
+ ) {
12
12
  padding: 0;
13
13
  font-size: var(--nk-text-sm);
14
14
  font-weight: var(--nk-font-weight-semibold);
15
15
  }
16
16
 
17
17
  :where(
18
- [data-nk="appearance-picker"] > [data-slot="appearance-picker-options"]
19
- ) {
18
+ [data-nk="appearance-picker"] > [data-slot="appearance-picker-options"]
19
+ ) {
20
20
  display: flex;
21
21
  flex-wrap: wrap;
22
22
  gap: calc(var(--nk-space) * 3);
@@ -24,16 +24,16 @@
24
24
  }
25
25
 
26
26
  :where(
27
- [data-nk="appearance-picker"][data-presentation="radios"]
28
- > [data-slot="appearance-picker-options"]
29
- ) {
27
+ [data-nk="appearance-picker"][data-presentation="radios"]
28
+ > [data-slot="appearance-picker-options"]
29
+ ) {
30
30
  display: grid;
31
31
  }
32
32
 
33
33
  :where(
34
- [data-nk="appearance-picker"][data-presentation="segmented"]
35
- > [data-slot="appearance-picker-options"]
36
- ) {
34
+ [data-nk="appearance-picker"][data-presentation="segmented"]
35
+ > [data-slot="appearance-picker-options"]
36
+ ) {
37
37
  display: inline-flex;
38
38
  gap: 0;
39
39
  padding: var(--nk-space);
@@ -51,8 +51,8 @@
51
51
  }
52
52
 
53
53
  :where(
54
- [data-nk="appearance-picker"] [data-slot="appearance-picker-control"]
55
- ) {
54
+ [data-nk="appearance-picker"] [data-slot="appearance-picker-control"]
55
+ ) {
56
56
  inline-size: 1.125rem;
57
57
  block-size: 1.125rem;
58
58
  margin: 0;
@@ -61,26 +61,26 @@
61
61
  }
62
62
 
63
63
  :where(
64
- [data-nk="appearance-picker"][data-presentation="segmented"]
65
- [data-slot="appearance-picker-option"]
66
- ) {
64
+ [data-nk="appearance-picker"][data-presentation="segmented"]
65
+ [data-slot="appearance-picker-option"]
66
+ ) {
67
67
  position: relative;
68
68
  padding-inline: calc(var(--nk-space) * 3);
69
69
  border-radius: calc(var(--nk-radius-md) - var(--nk-space));
70
70
  }
71
71
 
72
72
  :where(
73
- [data-nk="appearance-picker"][data-presentation="segmented"]
74
- [data-slot="appearance-picker-control"]
75
- ) {
73
+ [data-nk="appearance-picker"][data-presentation="segmented"]
74
+ [data-slot="appearance-picker-control"]
75
+ ) {
76
76
  position: absolute;
77
77
  opacity: 0;
78
78
  }
79
79
 
80
80
  :where(
81
- [data-nk="appearance-picker"][data-presentation="select"]
82
- > [data-slot="appearance-picker-select"]
83
- ) {
81
+ [data-nk="appearance-picker"][data-presentation="select"]
82
+ > [data-slot="appearance-picker-select"]
83
+ ) {
84
84
  display: block;
85
85
  inline-size: 100%;
86
86
  min-block-size: var(--nk-control-height-md);
@@ -96,21 +96,31 @@
96
96
 
97
97
  @layer nitro-kit.state {
98
98
  :where(
99
- [data-nk="appearance-picker"][data-presentation="segmented"][data-state="light"]
100
- [data-slot="appearance-picker-option"]:has([value="light"]),
101
- [data-nk="appearance-picker"][data-presentation="segmented"][data-state="dark"]
102
- [data-slot="appearance-picker-option"]:has([value="dark"]),
103
- [data-nk="appearance-picker"][data-presentation="segmented"][data-state="system"]
104
- [data-slot="appearance-picker-option"]:has([value="system"])
105
- ) {
99
+ [data-nk="appearance-picker"][data-presentation="segmented"]
100
+ [data-slot="appearance-picker-option"]:has(
101
+ [data-slot="appearance-picker-control"]:checked
102
+ )
103
+ ) {
106
104
  background: var(--nk-color-surface);
107
105
  box-shadow: var(--nk-shadow-sm);
108
106
  }
109
107
 
110
108
  :where(
111
- [data-nk="appearance-picker"]
112
- [data-slot="appearance-picker-control"]:focus-visible
113
- ) {
109
+ [data-nk="appearance-picker"]
110
+ [data-slot="appearance-picker-control"]:focus-visible
111
+ ) {
112
+ outline: var(--nk-focus-width) solid var(--nk-color-focus);
113
+ outline-offset: var(--nk-focus-offset);
114
+ }
115
+
116
+ /* The segmented control is visually hidden, so its focus ring must show on
117
+ the segment that contains it. */
118
+ :where(
119
+ [data-nk="appearance-picker"][data-presentation="segmented"]
120
+ [data-slot="appearance-picker-option"]:has(
121
+ [data-slot="appearance-picker-control"]:focus-visible
122
+ )
123
+ ) {
114
124
  outline: var(--nk-focus-width) solid var(--nk-color-focus);
115
125
  outline-offset: var(--nk-focus-offset);
116
126
  }
@@ -28,9 +28,9 @@
28
28
  }
29
29
 
30
30
  :where(
31
- [data-nk="avatar"] > [data-slot="avatar-fallback"],
32
- [data-nk="avatar"] > [data-slot="avatar-image"]
33
- ) {
31
+ [data-nk="avatar"] > [data-slot="avatar-fallback"],
32
+ [data-nk="avatar"] > [data-slot="avatar-image"]
33
+ ) {
34
34
  grid-area: 1 / 1;
35
35
  width: 100%;
36
36
  height: 100%;
@@ -1,21 +1,35 @@
1
1
  @layer nitro-kit.compound {
2
2
  :where([data-nk="avatar-stack"]) {
3
+ --_nk-avatar-stack-overlap: calc(var(--nk-space) * -3);
4
+
3
5
  display: inline-flex;
4
6
  align-items: center;
5
7
  vertical-align: middle;
6
8
  }
7
9
 
10
+ :where([data-nk="avatar-stack"][data-size="xs"]) {
11
+ --_nk-avatar-stack-overlap: calc(var(--nk-space) * -1.5);
12
+ }
13
+
14
+ :where([data-nk="avatar-stack"][data-size="sm"]) {
15
+ --_nk-avatar-stack-overlap: calc(var(--nk-space) * -2);
16
+ }
17
+
18
+ :where([data-nk="avatar-stack"][data-size="lg"]) {
19
+ --_nk-avatar-stack-overlap: calc(var(--nk-space) * -4);
20
+ }
21
+
8
22
  /* Stacked items overlap the ring of the item behind them, so a translucent
9
23
  fill would let that ring show through. Each item paints its own fill as a
10
24
  background image over an opaque surface underlay. */
11
25
  :where(
12
- [data-nk="avatar-stack"] > [data-slot="avatar-stack-avatar"],
13
- [data-nk="avatar-stack"] > [data-slot="avatar-stack-overflow"]
14
- ) {
26
+ [data-nk="avatar-stack"] > [data-slot="avatar-stack-avatar"],
27
+ [data-nk="avatar-stack"] > [data-slot="avatar-stack-overflow"]
28
+ ) {
15
29
  --_nk-avatar-stack-fill: var(--nk-color-muted);
16
30
 
17
31
  flex: none;
18
- margin-inline-start: calc(var(--nk-space) * -3);
32
+ margin-inline-start: var(--_nk-avatar-stack-overlap);
19
33
  background-color: var(--nk-color-surface);
20
34
  background-image: linear-gradient(
21
35
  var(--_nk-avatar-stack-fill),
@@ -25,15 +39,18 @@
25
39
  }
26
40
 
27
41
  :where(
28
- [data-nk="avatar-stack"] > [data-slot="avatar-stack-avatar"]:first-child,
29
- [data-nk="avatar-stack"] > [data-slot="avatar-stack-overflow"]:first-child
30
- ) {
42
+ [data-nk="avatar-stack"] > [data-slot="avatar-stack-avatar"]:first-child,
43
+ [data-nk="avatar-stack"] > [data-slot="avatar-stack-overflow"]:first-child
44
+ ) {
31
45
  margin-inline-start: 0;
32
46
  }
33
47
 
34
48
  :where([data-nk="avatar-stack"] > [data-slot="avatar-stack-overflow"]) {
35
49
  --_nk-avatar-stack-fill: var(--nk-color-elevated);
36
50
 
51
+ /* Preceding avatars are positioned, so the overflow chip must be too or
52
+ their paint order covers its leading "+". */
53
+ position: relative;
37
54
  display: inline-grid;
38
55
  place-items: center;
39
56
  font-weight: var(--nk-font-weight-semibold);
@@ -43,36 +60,36 @@
43
60
  }
44
61
 
45
62
  :where(
46
- [data-nk="avatar-stack"][data-size="xs"]
47
- > [data-slot="avatar-stack-overflow"]
48
- ) {
63
+ [data-nk="avatar-stack"][data-size="xs"]
64
+ > [data-slot="avatar-stack-overflow"]
65
+ ) {
49
66
  width: 1.5rem;
50
67
  height: 1.5rem;
51
68
  font-size: calc(var(--nk-text-xs) * 0.875);
52
69
  }
53
70
 
54
71
  :where(
55
- [data-nk="avatar-stack"][data-size="sm"]
56
- > [data-slot="avatar-stack-overflow"]
57
- ) {
72
+ [data-nk="avatar-stack"][data-size="sm"]
73
+ > [data-slot="avatar-stack-overflow"]
74
+ ) {
58
75
  width: 2rem;
59
76
  height: 2rem;
60
77
  font-size: var(--nk-text-xs);
61
78
  }
62
79
 
63
80
  :where(
64
- [data-nk="avatar-stack"][data-size="md"]
65
- > [data-slot="avatar-stack-overflow"]
66
- ) {
81
+ [data-nk="avatar-stack"][data-size="md"]
82
+ > [data-slot="avatar-stack-overflow"]
83
+ ) {
67
84
  width: 3rem;
68
85
  height: 3rem;
69
86
  font-size: var(--nk-text-sm);
70
87
  }
71
88
 
72
89
  :where(
73
- [data-nk="avatar-stack"][data-size="lg"]
74
- > [data-slot="avatar-stack-overflow"]
75
- ) {
90
+ [data-nk="avatar-stack"][data-size="lg"]
91
+ > [data-slot="avatar-stack-overflow"]
92
+ ) {
76
93
  width: 4rem;
77
94
  height: 4rem;
78
95
  font-size: var(--nk-text-base);
@@ -3,7 +3,7 @@
3
3
  --_nk-button-height: var(--nk-control-height-md);
4
4
  --_nk-button-padding: calc(var(--nk-space) * 4);
5
5
  --_nk-button-icon-padding: calc(var(--nk-space) * 3);
6
- --_nk-button-radius: var(--nk-radius-lg);
6
+ --_nk-button-radius: var(--nk-button-radius, var(--nk-radius-lg));
7
7
  --_nk-button-font-size: var(--nk-text-sm);
8
8
  --_nk-button-line-height: 1.25rem;
9
9
  --_nk-button-background: var(--nk-button-default-background);
@@ -52,10 +52,10 @@
52
52
  }
53
53
 
54
54
  :where(
55
- [data-nk="button"] > [data-slot="button-icon-start"],
56
- [data-nk="button"] > [data-slot="button-icon-end"],
57
- [data-nk="button"] > [data-slot="button-spinner"]
58
- ) {
55
+ [data-nk="button"] > [data-slot="button-icon-start"],
56
+ [data-nk="button"] > [data-slot="button-icon-end"],
57
+ [data-nk="button"] > [data-slot="button-spinner"]
58
+ ) {
59
59
  display: inline-flex;
60
60
  flex: none;
61
61
  }
@@ -66,18 +66,27 @@
66
66
  }
67
67
 
68
68
  :where([data-nk="button"] > [data-slot="button-label"]) {
69
+ display: inline-flex;
70
+ gap: calc(var(--nk-space) * 2);
71
+ align-items: center;
69
72
  min-inline-size: 0;
70
73
  overflow: hidden;
71
74
  text-overflow: ellipsis;
72
75
  }
73
76
 
77
+ /* text-overflow never applies inside a flex container, so text-only labels
78
+ become blocks to keep a visible ellipsis under pressure. */
79
+ :where([data-nk="button"] > [data-slot="button-label"]:not(:has(*))) {
80
+ display: block;
81
+ }
82
+
74
83
  :where(
75
- [data-nk="button"]:has(> [data-slot="button-icon-start"]),
76
- [data-nk="button"]:has(> [data-slot="button-spinner"]),
77
- [data-nk="button"]:has(
78
- > [data-slot="button-submission-spinner"][data-state="visible"]
79
- )
80
- ) {
84
+ [data-nk="button"]:has(> [data-slot="button-icon-start"]),
85
+ [data-nk="button"]:has(> [data-slot="button-spinner"]),
86
+ [data-nk="button"]:has(
87
+ > [data-slot="button-submission-spinner"][data-state="visible"]
88
+ )
89
+ ) {
81
90
  padding-inline-start: var(--_nk-button-icon-padding);
82
91
  }
83
92
 
@@ -86,12 +95,12 @@
86
95
  }
87
96
 
88
97
  :where(
89
- [data-nk="button"]:not(:has(> [data-slot="button-label"])):has(
90
- > [data-slot="button-icon-start"],
91
- > [data-slot="button-icon-end"],
92
- > [data-slot="button-spinner"]
93
- )
94
- ) {
98
+ [data-nk="button"]:not(:has(> [data-slot="button-label"])):has(
99
+ > [data-slot="button-icon-start"],
100
+ > [data-slot="button-icon-end"],
101
+ > [data-slot="button-spinner"]
102
+ )
103
+ ) {
95
104
  inline-size: var(--_nk-button-height);
96
105
  padding-inline: 0;
97
106
  }
@@ -114,8 +123,8 @@
114
123
  --_nk-button-border-width: 0;
115
124
  --_nk-button-border: transparent;
116
125
  --_nk-button-border-bottom: transparent;
117
- --_nk-button-shadow:
118
- inset 0 1px var(--nk-color-danger), inset 0 2px oklch(1 0 0 / 0.15);
126
+ --_nk-button-shadow: inset 0 1px var(--nk-color-danger),
127
+ inset 0 2px oklch(1 0 0 / 0.15);
119
128
  }
120
129
 
121
130
  :where([data-nk="button"][data-variant="ghost"]) {
@@ -124,10 +133,7 @@
124
133
  oklch(0.274 0.006 286.033 / 0.05),
125
134
  oklch(1 0 0 / 0.15)
126
135
  );
127
- --_nk-button-foreground: light-dark(
128
- oklch(0.274 0.006 286.033),
129
- oklch(1 0 0)
130
- );
136
+ --_nk-button-foreground: var(--nk-color-foreground);
131
137
  --_nk-button-border-width: 0;
132
138
  --_nk-button-border: transparent;
133
139
  --_nk-button-border-bottom: transparent;
@@ -136,18 +142,18 @@
136
142
 
137
143
  @media (prefers-color-scheme: dark) {
138
144
  :where(
139
- :root:not([data-theme])
140
- [data-nk="button"]:is(
141
- [data-variant="primary"],
142
- [data-variant="destructive"]
143
- )
144
- ) {
145
+ :root:not([data-theme])
146
+ [data-nk="button"]:is(
147
+ [data-variant="primary"],
148
+ [data-variant="destructive"]
149
+ )
150
+ ) {
145
151
  --_nk-button-border-width: 0;
146
152
  }
147
153
 
148
154
  :where(
149
- :root:not([data-theme]) [data-nk="button"][data-variant="destructive"]
150
- ) {
155
+ :root:not([data-theme]) [data-nk="button"][data-variant="destructive"]
156
+ ) {
151
157
  --_nk-button-shadow: none;
152
158
  }
153
159
  }
@@ -166,7 +172,7 @@
166
172
  --_nk-button-height: var(--nk-control-height-xs);
167
173
  --_nk-button-padding: calc(var(--nk-space) * 2);
168
174
  --_nk-button-icon-padding: var(--nk-space);
169
- --_nk-button-radius: var(--nk-radius-md);
175
+ --_nk-button-radius: var(--nk-button-radius, var(--nk-radius-md));
170
176
  --_nk-button-font-size: var(--nk-text-xs);
171
177
  --_nk-button-line-height: 1rem;
172
178
  --_nk-button-avatar-size: 1rem;
@@ -177,7 +183,7 @@
177
183
  --_nk-button-height: calc(var(--nk-space) * 8);
178
184
  --_nk-button-padding: calc(var(--nk-space) * 3);
179
185
  --_nk-button-icon-padding: calc(var(--nk-space) * 2);
180
- --_nk-button-radius: var(--nk-radius-md);
186
+ --_nk-button-radius: var(--nk-button-radius, var(--nk-radius-md));
181
187
  --_nk-button-avatar-size: 1.25rem;
182
188
  --_nk-button-avatar-font-size: calc(var(--nk-text-xs) * 0.875);
183
189
  }
@@ -199,7 +205,7 @@
199
205
  --_nk-button-height: var(--nk-control-height-xl);
200
206
  --_nk-button-padding: calc(var(--nk-space) * 8);
201
207
  --_nk-button-icon-padding: calc(var(--nk-space) * 7);
202
- --_nk-button-radius: var(--nk-radius-xl);
208
+ --_nk-button-radius: var(--nk-button-radius, var(--nk-radius-xl));
203
209
  --_nk-button-font-size: var(--nk-text-base);
204
210
  --_nk-button-line-height: 1.5rem;
205
211
  --_nk-button-avatar-size: 2rem;
@@ -221,22 +227,54 @@
221
227
  }
222
228
 
223
229
  :where(
224
- [data-nk="button"]:disabled,
225
- [data-nk="button"][aria-disabled="true"]
226
- ) {
230
+ [data-nk="button"]:disabled,
231
+ [data-nk="button"][aria-disabled="true"]
232
+ ) {
227
233
  pointer-events: none;
228
234
  cursor: default;
229
235
  opacity: 0.75;
230
236
  }
231
237
 
238
+ /* Dark surfaces need more than reduced opacity to read a saturated
239
+ destructive control as disabled. */
240
+ @media (prefers-color-scheme: dark) {
241
+ :where(
242
+ :root:not([data-theme])
243
+ [data-nk="button"][data-variant="destructive"]:is(
244
+ :disabled,
245
+ [aria-disabled="true"]
246
+ )
247
+ ) {
248
+ --_nk-button-background: color-mix(
249
+ in oklab,
250
+ var(--nk-color-danger) 40%,
251
+ var(--nk-color-surface)
252
+ );
253
+ }
254
+ }
255
+
256
+ :where(
257
+ [data-theme="dark"]
258
+ [data-nk="button"][data-variant="destructive"]:is(
259
+ :disabled,
260
+ [aria-disabled="true"]
261
+ )
262
+ ) {
263
+ --_nk-button-background: color-mix(
264
+ in oklab,
265
+ var(--nk-color-danger) 40%,
266
+ var(--nk-color-surface)
267
+ );
268
+ }
269
+
232
270
  :where([data-nk="button"][aria-busy="true"] > [data-slot="button-spinner"]) {
233
271
  animation: nk-button-spin 1s linear infinite;
234
272
  }
235
273
 
236
274
  :where(
237
- [data-nk="button"]
238
- > [data-slot="button-submission-spinner"][data-state="visible"]
239
- ) {
275
+ [data-nk="button"]
276
+ > [data-slot="button-submission-spinner"][data-state="visible"]
277
+ ) {
240
278
  display: inline-flex;
241
279
  animation: nk-button-spin 1s linear infinite;
242
280
  }
@@ -249,30 +287,17 @@
249
287
 
250
288
  @media (prefers-reduced-motion: reduce) {
251
289
  :where(
252
- [data-nk="button"][aria-busy="true"] > [data-slot="button-spinner"],
253
- [data-nk="button"]
254
- > [data-slot="button-submission-spinner"][data-state="visible"]
255
- ) {
290
+ [data-nk="button"][aria-busy="true"] > [data-slot="button-spinner"],
291
+ [data-nk="button"]
292
+ > [data-slot="button-submission-spinner"][data-state="visible"]
293
+ ) {
256
294
  animation: none;
257
295
  }
258
296
  }
259
297
  }
260
298
 
261
299
  @layer nitro-kit.compound {
262
- :where(
263
- [data-nk="button"]
264
- > [data-slot="button-label"]:has(> [data-nk="avatar"])
265
- ) {
266
- display: inline-flex;
267
- gap: calc(var(--nk-space) * 2);
268
- align-items: center;
269
- }
270
-
271
- :where(
272
- [data-nk="button"]
273
- > [data-slot="button-label"]
274
- > [data-nk="avatar"]
275
- ) {
300
+ :where([data-nk="button"] > [data-slot="button-label"] > [data-nk="avatar"]) {
276
301
  width: min(var(--_nk-avatar-size), var(--_nk-button-avatar-size));
277
302
  height: min(var(--_nk-avatar-size), var(--_nk-button-avatar-size));
278
303
  font-size: min(
@@ -5,6 +5,8 @@
5
5
  align-items: stretch;
6
6
  max-inline-size: 100%;
7
7
  overflow-x: auto;
8
+ scrollbar-width: thin;
9
+ scrollbar-color: var(--nk-color-border) transparent;
8
10
  vertical-align: middle;
9
11
  }
10
12
 
@@ -16,38 +18,41 @@
16
18
  }
17
19
 
18
20
  :where(
19
- [data-nk="button-group"]
20
- > [data-slot="button-group-button"]:not(:first-child)
21
- ) {
21
+ [data-nk="button-group"]
22
+ > [data-slot="button-group-button"]:not(:first-child)
23
+ ) {
22
24
  margin-inline-start: calc(var(--nk-border-width) * -1);
23
25
  }
24
26
 
25
27
  :where(
26
- [data-nk="button-group"]
27
- > [data-slot="button-group-button"]:not(:only-child)
28
- ) {
28
+ [data-nk="button-group"]
29
+ > [data-slot="button-group-button"]:not(:only-child)
30
+ ) {
29
31
  border-radius: 0;
30
32
  }
31
33
 
32
34
  :where(
33
- [data-nk="button-group"]
34
- > [data-slot="button-group-button"]:first-child:not(:only-child)
35
- ) {
35
+ [data-nk="button-group"]
36
+ > [data-slot="button-group-button"]:first-child:not(:only-child)
37
+ ) {
36
38
  border-start-start-radius: var(--_nk-button-radius);
37
39
  border-end-start-radius: var(--_nk-button-radius);
38
40
  }
39
41
 
40
42
  :where(
41
- [data-nk="button-group"]
42
- > [data-slot="button-group-button"]:last-child:not(:only-child)
43
- ) {
43
+ [data-nk="button-group"]
44
+ > [data-slot="button-group-button"]:last-child:not(:only-child)
45
+ ) {
44
46
  border-start-end-radius: var(--_nk-button-radius);
45
47
  border-end-end-radius: var(--_nk-button-radius);
46
48
  }
47
49
 
48
50
  :where(
49
- [data-nk="button-group"] > [data-slot="button-group-button"]:focus-visible
50
- ) {
51
+ [data-nk="button-group"] > [data-slot="button-group-button"]:focus-visible
52
+ ) {
51
53
  z-index: 1;
54
+ /* The scroll container would clip an offset ring at its edges, so member
55
+ focus rings draw inside the member. */
56
+ outline-offset: calc(var(--nk-focus-offset) * -1);
52
57
  }
53
58
  }
@@ -16,7 +16,7 @@
16
16
 
17
17
  :where([data-nk="card"] > [data-slot="card-title"]) {
18
18
  font-size: var(--nk-text-lg);
19
- font-weight: var(--nk-font-weight-semibold);
19
+ font-weight: var(--nk-font-weight-bold);
20
20
  line-height: var(--nk-leading-tight);
21
21
  text-wrap: balance;
22
22
  }
@@ -53,13 +53,19 @@
53
53
  border-start-end-radius: calc(var(--nk-radius-lg) - var(--nk-border-width));
54
54
  }
55
55
 
56
+ :where([data-nk="card"] > [data-slot="card-full"]:last-child) {
57
+ margin-block-end: calc(var(--_nk-card-padding) * -1);
58
+ border-end-start-radius: calc(var(--nk-radius-lg) - var(--nk-border-width));
59
+ border-end-end-radius: calc(var(--nk-radius-lg) - var(--nk-border-width));
60
+ }
61
+
56
62
  /* Full-width media fills the card edge to edge, so the card owns the corner
57
63
  radius and the media drops its own. */
58
64
  :where(
59
- [data-nk="card"]
60
- > [data-slot="card-full"]
61
- :is(img, [data-nk="progressive-image"])
62
- ) {
65
+ [data-nk="card"]
66
+ > [data-slot="card-full"]
67
+ :is(img, [data-nk="progressive-image"])
68
+ ) {
63
69
  border-radius: 0;
64
70
  }
65
71