1/* Button — multi-class API
2 *
3 * Compose:
4 * <button class="btn btn-primary"> default size, primary
5 * <button class="btn btn-sm btn-outline"> small outline
6 * <button class="btn btn-icon btn-ghost"> square icon-only ghost
7 * <button class="btn btn-lg btn-icon btn-danger">
8 *
9 * Axes are orthogonal:
10 * • size: default | btn-sm | btn-lg
11 * • shape: (rect) | btn-icon (square)
12 * • color: btn-{primary,secondary,outline,ghost,link,success,warning,danger,info}
13 *
14 * Sizes/shapes are nested under `.admin-btn` so compound selectors win the
15 * specificity battle against the base rule's `:has(>svg)` adjustment
16 * — that's why size/icon overrides live inside the .admin-btn block.
17 *
18 * Color variants live as standalone rules so they're orthogonal to
19 * size and trivially extended.
20 *
21 * Status buttons (success/warning/danger/info) render at 70% bg in dark
22 * mode so the saturated hue mixes into a softer tone — the foreground
23 * tokens already give plenty of contrast at full opacity. Tweak the
24 * `/70` and `/60` modifiers to taste.
25 */
26@layer components {
27 .admin-btn {
28 @apply focus-control inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none cursor-pointer;
29 border-radius: var(--radius-button);
30 /* default size */
31 @apply gap-2 h-9 px-4 py-2;
32 &:has(>svg) { @apply px-3; }
33
34 &.admin-btn-sm {
35 @apply gap-1.5 h-8 px-3 py-0;
36 &:has(>svg) { @apply px-2.5; }
37 }
38 &.admin-btn-lg {
39 @apply h-10 px-6 py-2;
40 &:has(>svg) { @apply px-4; }
41 }
42
43 &.admin-btn-icon { @apply size-9 p-0; }
44 &.admin-btn-sm.admin-btn-icon { @apply size-8; }
45 &.admin-btn-lg.admin-btn-icon { @apply size-10; }
46 }
47
48 .admin-btn-primary {
49 @apply bg-admin-primary text-admin-primary-foreground shadow-xs hover:bg-admin-primary/90;
50 &[aria-pressed='true'] { @apply bg-admin-primary/90; }
51 }
52 .admin-btn-secondary {
53 @apply bg-admin-secondary text-admin-secondary-foreground shadow-xs;
54 &:hover,
55 &[aria-pressed='true'] { @apply bg-admin-secondary/80; }
56 }
57 .admin-btn-outline {
58 @apply border bg-transparent shadow-xs;
59 &:hover,
60 &[aria-pressed='true'] { @apply bg-admin-accent text-admin-accent-foreground dark:bg-admin-accent/50; }
61 }
62 .admin-btn-ghost {
63 &:hover,
64 &[aria-pressed='true'] { @apply bg-admin-accent text-admin-accent-foreground dark:bg-admin-accent/50; }
65 }
66 .admin-btn-link {
67 @apply text-admin-primary underline-offset-4;
68 &:hover,
69 &[aria-pressed='true'] { @apply hover:underline; }
70 }
71 .admin-btn-success {
72 @apply bg-admin-success text-admin-success-foreground shadow-xs focus-visible:ring-admin-success/20 dark:focus-visible:ring-admin-success/40 dark:bg-admin-success/70;
73 &:hover,
74 &[aria-pressed='true'] { @apply bg-admin-success/90 dark:bg-admin-success/60; }
75 }
76 .admin-btn-warning {
77 @apply bg-admin-warning text-admin-warning-foreground shadow-xs focus-visible:ring-admin-warning/20 dark:focus-visible:ring-admin-warning/40 dark:bg-admin-warning/70;
78 &:hover,
79 &[aria-pressed='true'] { @apply bg-admin-warning/90 dark:bg-admin-warning/60; }
80 }
81 .admin-btn-danger {
82 @apply bg-admin-danger text-admin-danger-foreground shadow-xs focus-visible:ring-admin-danger/20 dark:focus-visible:ring-admin-danger/40 dark:bg-admin-danger/70;
83 &:hover,
84 &[aria-pressed='true'] { @apply bg-admin-danger/90 dark:bg-admin-danger/60; }
85 }
86 .admin-btn-info {
87 @apply bg-admin-info text-admin-info-foreground shadow-xs focus-visible:ring-admin-info/20 dark:focus-visible:ring-admin-info/40 dark:bg-admin-info/70;
88 &:hover,
89 &[aria-pressed='true'] { @apply bg-admin-info/90 dark:bg-admin-info/60; }
90 }
91}
92
93/* Button Group */
94@layer components {
95 .admin-button-group {
96 @apply inline-flex w-fit items-stretch;
97 /* isolate */
98
99 > *:focus-visible,
100 > :is(.admin-dropdown-menu, .admin-popover, .admin-select) > button:focus-visible {
101 @apply relative z-10;
102 }
103
104 > hr[role='separator'] {
105 @apply w-0 h-auto self-stretch border border-admin-input shrink-0 m-0;
106 }
107
108 &:not([data-orientation='vertical']) {
109 > *:not(:first-child),
110 > :is(.admin-dropdown-menu, .admin-popover, .admin-select):not(:first-child) > button {
111 @apply rounded-l-none border-l-0;
112 }
113 > *:not(:last-child),
114 > :is(.admin-dropdown-menu, .admin-popover, .admin-select):not(:last-child) > button {
115 @apply rounded-r-none;
116 }
117 }
118 &[data-orientation='vertical'] {
119 @apply flex-col;
120
121 > hr[role='separator'] {
122 @apply w-auto h-px;
123 }
124
125 > *:not(:first-child),
126 > :is(.admin-dropdown-menu, .admin-popover, .admin-select):not(:first-child) > button {
127 @apply rounded-t-none border-t-0;
128 }
129 > *:not(:last-child),
130 > :is(.admin-dropdown-menu, .admin-popover, .admin-select):not(:last-child) > button {
131 @apply rounded-b-none;
132 }
133 }
134 }
135}