spry-theme-modern.css 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /*
  2. * Spry CSS theme: "modern"
  3. * ------------------------
  4. * Contemporary premium design system — Linear/Vercel/Stripe register.
  5. * Signatures: mono micro-labels (h6/badges/table heads/hints), filled
  6. * gray input wells that bloom white on focus, flat hairline cards that
  7. * lift on hover, borderless tables with row dividers, left-accent-bar
  8. * alerts, and a sheen + soft glow primary button.
  9. * Light-only: color-scheme stays light and the user's dark preference is ignored.
  10. */
  11. [data-theme="modern"],
  12. html:has(#spry-theme-modern:checked) {
  13. color-scheme: light;
  14. /* ---- Palette (soft tints & alert washes derive automatically) ---- */
  15. --spry-bg: #fafafa; /* near-white page */
  16. --spry-bg-alt: #f4f4f5; /* alternate bands, hovers */
  17. --spry-bg-inset: #efeff2; /* wells: code blocks, disabled fills, tracks */
  18. --spry-surface: #ffffff; /* cards, popovers, inputs on focus */
  19. --spry-surface-hover: #fafafa;
  20. --spry-fg: #18181b; /* near-black text */
  21. --spry-fg-muted: #52525b; /* secondary text (AA on bg) */
  22. --spry-border: #e4e4e7; /* hairlines */
  23. --spry-border-strong: #d4d4d8; /* hover borders, kbd, switch track */
  24. --spry-accent: #5b5bd6; /* vivid indigo */
  25. --spry-accent-hover: #4949c3;
  26. --spry-accent-fg: #ffffff; /* 5.4:1 on accent */
  27. --spry-danger: #d92d20;
  28. --spry-success: #15803d;
  29. --spry-warning: #a16207;
  30. --spry-info: #0369a1;
  31. --spry-backdrop: rgb(24 24 27 / 0.35);
  32. --spry-link: var(--spry-accent);
  33. --spry-link-hover: var(--spry-accent-hover);
  34. /* Soft halo rings — solid-accent borders/underlines carry the 3:1 contrast */
  35. --spry-focus-ring: 0 0 0 2px color-mix(in srgb, var(--spry-accent) 40%, transparent);
  36. /* ---- Typography ---- */
  37. --spry-font: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  38. --spry-font-heading: var(--spry-font);
  39. --spry-font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  40. --spry-font-size: 1rem;
  41. --spry-line-height: 1.6;
  42. --spry-heading-weight: 650;
  43. /* ---- Shape, depth, motion: 8px controls / 12px cards / 16px overlays ---- */
  44. --spry-radius: 0.75rem; /* cards, btn-lg */
  45. --spry-radius-sm: 0.5rem; /* buttons, inputs, alerts */
  46. --spry-radius-lg: 1rem; /* dialogs, popovers */
  47. --spry-radius-pill: 999px;
  48. --spry-border-width: 1px;
  49. --spry-shadow: 0 2px 4px rgb(24 24 27 / 0.04), 0 8px 24px -8px rgb(24 24 27 / 0.08);
  50. --spry-shadow-lg: 0 8px 20px -6px rgb(24 24 27 / 0.12), 0 20px 48px -12px rgb(24 24 27 / 0.16);
  51. --spry-glass: none;
  52. --spry-transition: 150ms cubic-bezier(0.25, 0.6, 0.35, 1);
  53. --spry-dialog-duration: 180ms;
  54. --spry-dialog-ease: cubic-bezier(0.2, 0.9, 0.3, 1);
  55. --spry-dialog-in: translateY(0.5rem) scale(0.98);
  56. /* ---- Controls: filled gray wells, no border until hover/focus ---- */
  57. --spry-control-bg: #f4f4f5;
  58. --spry-control-fg: var(--spry-fg);
  59. --spry-control-border: transparent;
  60. --spry-control-radius: var(--spry-radius-sm);
  61. --spry-control-padding-x: 0.7rem;
  62. --spry-control-padding-y: 0.5rem;
  63. /* ---- Buttons ---- */
  64. --spry-btn-bg: var(--spry-surface);
  65. --spry-btn-fg: var(--spry-fg);
  66. --spry-btn-border: var(--spry-border);
  67. --spry-btn-bg-hover: var(--spry-bg-alt);
  68. --spry-btn-bg-active: var(--spry-bg-inset);
  69. /* ---- Cards / tables / progress / popovers ---- */
  70. --spry-card-bg: var(--spry-surface);
  71. --spry-card-border: var(--spry-border);
  72. --spry-card-padding: 1.5rem;
  73. --spry-card-shadow: none; /* flat at rest, lift on hover */
  74. --spry-table-header-bg: transparent; /* airy headerless-column look */
  75. --spry-table-hover: var(--spry-bg-alt);
  76. --spry-progress-bg: var(--spry-bg-inset);
  77. --spry-progress-fg: var(--spry-accent);
  78. --spry-popover-bg: var(--spry-surface);
  79. --spry-popover-border: var(--spry-border);
  80. --spry-popover-shadow: var(--spry-shadow-lg);
  81. /* ---- Structural overrides (nested, active only under this scope) ---- */
  82. /* Barely-there dot-grid texture — the "modern dev tool" page floor */
  83. & body {
  84. background:
  85. radial-gradient(rgb(24 24 27 / 0.04) 1px, transparent 1px) 0 0 / 24px 24px,
  86. var(--spry-bg);
  87. }
  88. /* Tight display-type tracking for the marketing-page look */
  89. & :where(h1, h2, h3, .h1, .h2, .h3) {
  90. letter-spacing: -0.022em;
  91. }
  92. /* Signature: mono micro-labels */
  93. & :where(h6, .h6) {
  94. font-family: var(--spry-font-mono);
  95. letter-spacing: 0.09em;
  96. }
  97. & .badge {
  98. font-family: var(--spry-font-mono);
  99. text-transform: uppercase;
  100. letter-spacing: 0.06em;
  101. font-weight: 550;
  102. padding: 0.15em 0.7em;
  103. }
  104. & .hint {
  105. font-family: var(--spry-font-mono);
  106. font-size: 0.8em;
  107. }
  108. /* Global soft focus halo (inputs keep their own ring via the core) */
  109. & :where(:focus-visible) {
  110. outline: 3px solid color-mix(in srgb, var(--spry-accent) 35%, transparent);
  111. outline-offset: 2px;
  112. }
  113. /* Buttons: shared motion incl. transform */
  114. & .btn {
  115. transition: background-color var(--spry-transition), border-color var(--spry-transition),
  116. color var(--spry-transition), box-shadow var(--spry-transition),
  117. transform var(--spry-transition);
  118. }
  119. & .btn:not(.btn-ghost):not(.btn-link):hover {
  120. transform: translateY(-1px);
  121. }
  122. & .btn:not(.btn-ghost):not(.btn-link):active {
  123. transform: translateY(0);
  124. }
  125. /* Primary: hairline top sheen + accent glow that deepens on hover */
  126. & .btn-primary {
  127. box-shadow:
  128. inset 0 1px 0 rgb(255 255 255 / 0.2),
  129. 0 1px 2px color-mix(in srgb, var(--spry-accent) 30%, transparent);
  130. }
  131. & .btn-primary:hover {
  132. box-shadow:
  133. inset 0 1px 0 rgb(255 255 255 / 0.22),
  134. 0 2px 4px color-mix(in srgb, var(--spry-accent) 32%, transparent),
  135. 0 8px 20px -6px color-mix(in srgb, var(--spry-accent) 45%, transparent);
  136. }
  137. & .btn-primary:active {
  138. box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.12);
  139. }
  140. /* Ghost buttons gain a hairline frame on hover */
  141. & .btn-ghost:hover {
  142. border-color: var(--spry-border);
  143. }
  144. /* Inputs: filled wells turn white + ring on focus (ring/border via core) */
  145. & :where(input, select, textarea) {
  146. transition: background-color var(--spry-transition),
  147. border-color var(--spry-transition), box-shadow var(--spry-transition);
  148. }
  149. & :where(input[type="text"], input[type="email"], input[type="password"],
  150. input[type="number"], input[type="search"], input[type="tel"], input[type="url"],
  151. input[type="date"], input[type="time"], input[type="datetime-local"],
  152. input[type="month"], input[type="week"], select, textarea, .input):focus:not([readonly]) {
  153. background: var(--spry-surface);
  154. }
  155. /* Cards: flat hairline at rest, soft lift on hover */
  156. & .card {
  157. transition: transform var(--spry-transition), box-shadow var(--spry-transition),
  158. border-color var(--spry-transition);
  159. }
  160. & .card:not(.card-flat):hover {
  161. transform: translateY(-2px);
  162. border-color: var(--spry-border-strong);
  163. box-shadow: 0 4px 12px -2px rgb(24 24 27 / 0.06), 0 12px 32px -12px rgb(24 24 27 / 0.12);
  164. }
  165. & :where(.card-header, .card-footer) {
  166. background: transparent;
  167. }
  168. /* Tables: no frame, hairline row dividers, mono column heads */
  169. & :where(table) {
  170. border: 0;
  171. }
  172. & :where(thead th) {
  173. font-family: var(--spry-font-mono);
  174. font-size: 0.72em;
  175. letter-spacing: 0.08em;
  176. border-bottom: var(--spry-border-width) solid var(--spry-border-strong);
  177. }
  178. /* Alerts: soft wash + colored inset edge instead of a full frame */
  179. & :where(.alert) {
  180. border: 0;
  181. border-radius: var(--spry-radius-sm);
  182. box-shadow: inset 2px 0 0 var(--spry-border-strong);
  183. }
  184. & .alert-danger { box-shadow: inset 2px 0 0 var(--spry-danger); color: #b3180e; }
  185. & .alert-success { box-shadow: inset 2px 0 0 var(--spry-success); color: #0f7a34; }
  186. & .alert-warning { box-shadow: inset 2px 0 0 var(--spry-warning); color: #855600; }
  187. & .alert-info { box-shadow: inset 2px 0 0 var(--spry-info); color: #075e94; }
  188. & .nav a.active { color: #4343c5; }
  189. & .badge { color: #4343c5; }
  190. & .hero-eyebrow { color: #4343c5; }
  191. /* Dialogs: minimal titlebar (bold text + hairline), circular close */
  192. & .dialog-titlebar {
  193. background: transparent;
  194. border-bottom: var(--spry-border-width) solid var(--spry-border);
  195. }
  196. & .dialog-title {
  197. font-size: 1rem;
  198. }
  199. & .dialog-close {
  200. border-radius: var(--spry-radius-pill);
  201. }
  202. & .dialog-footer {
  203. background: transparent;
  204. }
  205. /* Switch: slightly larger, floating white knob */
  206. & .switch .track {
  207. width: 2.75rem;
  208. height: 1.5rem;
  209. }
  210. & .switch .track::after {
  211. top: 0.2rem;
  212. left: 0.2rem;
  213. width: 1.1rem;
  214. height: 1.1rem;
  215. box-shadow: 0 1px 3px rgb(24 24 27 / 0.3), 0 0 0 1px rgb(24 24 27 / 0.05);
  216. }
  217. & .switch input:checked + .track::after {
  218. translate: 1.25rem 0;
  219. }
  220. /* Slim progress bars */
  221. & progress {
  222. height: 0.45rem;
  223. }
  224. /* Indeterminate: theme-explicit so it can never be shadowed */
  225. & progress:indeterminate {
  226. /* cap = half the slim 0.45rem bar */
  227. --spry-pulse-cap: 0.23em;
  228. background:
  229. radial-gradient(var(--spry-pulse-cap) var(--spry-pulse-cap) at var(--spry-pulse-cap) 50%, var(--spry-accent) 97%, transparent 100%) 0% 50% / 25% 100% no-repeat,
  230. radial-gradient(var(--spry-pulse-cap) var(--spry-pulse-cap) at calc(100% - var(--spry-pulse-cap)) 50%, var(--spry-accent) 97%, transparent 100%) 0% 50% / 25% 100% no-repeat,
  231. linear-gradient(90deg, transparent 0 var(--spry-pulse-cap), var(--spry-accent) var(--spry-pulse-cap) calc(100% - var(--spry-pulse-cap)), transparent calc(100% - var(--spry-pulse-cap))) 0% 50% / 25% 100% no-repeat,
  232. var(--spry-bg-inset);
  233. animation: spry-pulse 1.4s linear infinite alternate;
  234. }
  235. }