index.css 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. @import "tailwindcss";
  2. /* Inter, self-hosted (#1460). Bambuddy is a local-first, offline-capable PWA;
  3. pulling the font from fonts.googleapis.com at runtime broke the install on
  4. Android (CSP-blocked cross-origin fetch) and meant the UI couldn't render
  5. offline. These are the variable woff2 files Google Fonts serves — one file
  6. covers every weight via the variable axis, so font-weight is a 100-900 range.
  7. Files live in frontend/public/fonts/ and are served same-origin. */
  8. @font-face {
  9. font-family: 'Inter';
  10. font-style: normal;
  11. font-weight: 100 900;
  12. font-display: swap;
  13. src: url('/fonts/inter-latin.woff2') format('woff2');
  14. unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  15. }
  16. @font-face {
  17. font-family: 'Inter';
  18. font-style: normal;
  19. font-weight: 100 900;
  20. font-display: swap;
  21. src: url('/fonts/inter-latin-ext.woff2') format('woff2');
  22. unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  23. }
  24. /* Enable class-based dark mode for Tailwind v4 */
  25. @custom-variant dark (&:where(.dark, .dark *));
  26. @theme {
  27. /* Accent colors - use CSS variables for theming */
  28. --color-bambu-green: var(--accent);
  29. --color-bambu-green-light: var(--accent-light);
  30. --color-bambu-green-dark: var(--accent-dark);
  31. /* Semantic status colors - fixed, don't change with accent */
  32. --color-status-ok: var(--status-ok);
  33. --color-status-error: var(--status-error);
  34. --color-status-warning: var(--status-warning);
  35. /* Theme-aware colors via CSS variables */
  36. --color-bambu-dark: var(--bg-primary);
  37. --color-bambu-dark-secondary: var(--bg-secondary);
  38. --color-bambu-dark-tertiary: var(--bg-tertiary);
  39. --color-bambu-gray: var(--text-muted);
  40. --color-bambu-gray-light: var(--text-secondary);
  41. --color-bambu-gray-dark: var(--text-tertiary);
  42. }
  43. /* ============================================
  44. BASE DEFAULTS
  45. ============================================ */
  46. html {
  47. font-size: 14.4px;
  48. }
  49. :root {
  50. /* Default accent color (green) */
  51. --accent: #00ae42;
  52. --accent-light: #00c64d;
  53. --accent-dark: #009438;
  54. /* Semantic status colors - these never change with accent theme */
  55. --status-ok: #22c55e; /* green-500 - always green for success/online/ok */
  56. --status-error: #ef4444; /* red-500 - always red for error/offline/failed */
  57. --status-warning: #f59e0b; /* amber-500 - always amber for warnings */
  58. /* Default light mode background (neutral) */
  59. --bg-primary: #f5f5f5;
  60. --bg-secondary: #ffffff;
  61. --bg-tertiary: #e5e5e5;
  62. --text-primary: #1a1a1a;
  63. --text-secondary: #4a4a4a;
  64. --text-muted: #6b6b6b;
  65. --text-tertiary: #808080;
  66. --border-color: #d4d4d4;
  67. /* Default style (classic) */
  68. --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  69. --glow-color: transparent;
  70. font-family: 'Inter', system-ui, sans-serif;
  71. line-height: 1.5;
  72. font-weight: 400;
  73. font-synthesis: none;
  74. text-rendering: optimizeLegibility;
  75. -webkit-font-smoothing: antialiased;
  76. -moz-osx-font-smoothing: grayscale;
  77. }
  78. /* Dark mode base */
  79. .dark {
  80. --bg-primary: #1a1a1a;
  81. --bg-secondary: #2d2d2d;
  82. --bg-tertiary: #3d3d3d;
  83. --text-primary: #ffffff;
  84. --text-secondary: #a0a0a0;
  85. --text-muted: #808080;
  86. --text-tertiary: #4a4a4a;
  87. --border-color: #3d3d3d;
  88. --card-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  89. }
  90. /* ============================================
  91. LAYER 1: BACKGROUND PALETTES
  92. ============================================ */
  93. /* Light mode backgrounds */
  94. .bg-neutral {
  95. /* Default - already set in :root */
  96. }
  97. .bg-warm {
  98. --bg-primary: #faf8f5;
  99. --bg-secondary: #fffefa;
  100. --bg-tertiary: #e8e4dd;
  101. --text-primary: #2d2a26;
  102. --text-secondary: #5c5750;
  103. --text-muted: #7a756c;
  104. --text-tertiary: #9a9590;
  105. --border-color: #d8d4cc;
  106. }
  107. .bg-cool {
  108. --bg-primary: #f0f4f8;
  109. --bg-secondary: #ffffff;
  110. --bg-tertiary: #dce4ec;
  111. --text-primary: #1a2530;
  112. --text-secondary: #4a5568;
  113. --text-muted: #6b7a8a;
  114. --text-tertiary: #8a9aaa;
  115. --border-color: #c8d4e0;
  116. }
  117. /* Dark mode backgrounds */
  118. .dark.bg-neutral {
  119. --bg-primary: #1a1a1a;
  120. --bg-secondary: #2d2d2d;
  121. --bg-tertiary: #3d3d3d;
  122. --text-primary: #ffffff;
  123. --text-secondary: #a0a0a0;
  124. --text-muted: #808080;
  125. --text-tertiary: #4a4a4a;
  126. --border-color: #3d3d3d;
  127. }
  128. .dark.bg-warm {
  129. --bg-primary: #1c1a18;
  130. --bg-secondary: #2e2a26;
  131. --bg-tertiary: #3e3a36;
  132. --text-primary: #f5f0ea;
  133. --text-secondary: #b0a898;
  134. --text-muted: #8a8278;
  135. --text-tertiary: #5a5248;
  136. --border-color: #3e3a36;
  137. }
  138. .dark.bg-cool {
  139. --bg-primary: #181c20;
  140. --bg-secondary: #262c32;
  141. --bg-tertiary: #363e46;
  142. --text-primary: #f0f4f8;
  143. --text-secondary: #98a8b8;
  144. --text-muted: #788898;
  145. --text-tertiary: #4a5a6a;
  146. --border-color: #363e46;
  147. }
  148. .dark.bg-oled {
  149. --bg-primary: #000000;
  150. --bg-secondary: #141414;
  151. --bg-tertiary: #1f1f1f;
  152. --text-primary: #ffffff;
  153. --text-secondary: #a0a0a0;
  154. --text-muted: #707070;
  155. --text-tertiary: #404040;
  156. --border-color: #2a2a2a;
  157. }
  158. .dark.bg-slate {
  159. --bg-primary: #0f172a;
  160. --bg-secondary: #1e293b;
  161. --bg-tertiary: #334155;
  162. --text-primary: #f1f5f9;
  163. --text-secondary: #94a3b8;
  164. --text-muted: #64748b;
  165. --text-tertiary: #475569;
  166. --border-color: #334155;
  167. }
  168. .dark.bg-forest {
  169. --bg-primary: #121a16;
  170. --bg-secondary: #1c2a22;
  171. --bg-tertiary: #2a3d30;
  172. --text-primary: #e8f5ec;
  173. --text-secondary: #8aa894;
  174. --text-muted: #6a8874;
  175. --text-tertiary: #4a6854;
  176. --border-color: #2a3d30;
  177. }
  178. /* Printer card control buttons: stack only when they clip */
  179. .printer-control-buttons-container {
  180. container-type: inline-size;
  181. }
  182. @container (max-width: 220px) {
  183. .printer-control-buttons {
  184. flex-direction: column;
  185. align-items: stretch;
  186. }
  187. .printer-control-buttons > button {
  188. width: 100%;
  189. }
  190. }
  191. /* ============================================
  192. LAYER 2: STYLE EFFECTS
  193. ============================================ */
  194. /* Classic - default, clean minimal shadows */
  195. .style-classic {
  196. /* Uses default shadows from :root and .dark */
  197. }
  198. /* Glow - accent-colored glow effects on cards */
  199. .style-glow {
  200. --card-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 0 25px color-mix(in srgb, var(--accent) 12%, transparent);
  201. }
  202. .dark.style-glow {
  203. --card-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), 0 0 40px color-mix(in srgb, var(--accent) 15%, transparent);
  204. }
  205. /* Vibrant - dramatic deep shadows, more contrast */
  206. .style-vibrant {
  207. --card-shadow: 0 8px 30px rgba(0, 0, 0, 0.15), 0 2px 8px rgba(0, 0, 0, 0.1);
  208. }
  209. .dark.style-vibrant {
  210. --card-shadow: 0 10px 40px rgba(0, 0, 0, 0.6), 0 4px 12px rgba(0, 0, 0, 0.4);
  211. }
  212. /* ============================================
  213. LAYER 3: ACCENT COLORS
  214. ============================================ */
  215. .accent-green {
  216. --accent: #00ae42;
  217. --accent-light: #00c64d;
  218. --accent-dark: #009438;
  219. }
  220. .accent-teal {
  221. --accent: #14b8a6;
  222. --accent-light: #2dd4bf;
  223. --accent-dark: #0d9488;
  224. }
  225. .accent-blue {
  226. --accent: #3b82f6;
  227. --accent-light: #60a5fa;
  228. --accent-dark: #2563eb;
  229. }
  230. .accent-orange {
  231. --accent: #f97316;
  232. --accent-light: #fb923c;
  233. --accent-dark: #ea580c;
  234. }
  235. .accent-purple {
  236. --accent: #8b5cf6;
  237. --accent-light: #a78bfa;
  238. --accent-dark: #7c3aed;
  239. }
  240. .accent-red {
  241. --accent: #ef4444;
  242. --accent-light: #f87171;
  243. --accent-dark: #dc2626;
  244. }
  245. body {
  246. background-color: var(--bg-primary);
  247. color: var(--text-primary);
  248. margin: 0;
  249. min-height: 100vh;
  250. transition: background-color 0.2s ease, color 0.2s ease;
  251. }
  252. #root {
  253. min-height: 100vh;
  254. }
  255. /* Override text-white to be theme-aware */
  256. .text-white {
  257. color: var(--text-primary);
  258. }
  259. /* Smooth transitions for theme changes */
  260. .bg-bambu-dark,
  261. .bg-bambu-dark-secondary,
  262. .bg-bambu-dark-tertiary,
  263. .border-bambu-dark-tertiary {
  264. transition: background-color 0.2s ease, border-color 0.2s ease;
  265. }
  266. /* Toast slide-in animation */
  267. @keyframes slide-in {
  268. from {
  269. transform: translateX(100%);
  270. opacity: 0;
  271. }
  272. to {
  273. transform: translateX(0);
  274. opacity: 1;
  275. }
  276. }
  277. .animate-slide-in {
  278. animation: slide-in 0.2s ease-out;
  279. }
  280. /* Theme-aware icon inversion - only invert in dark mode */
  281. .icon-theme {
  282. opacity: 0.5;
  283. }
  284. .dark .icon-theme {
  285. filter: invert(1);
  286. opacity: 0.4;
  287. }
  288. /* Green-colored icon for active status indicators */
  289. .icon-green {
  290. filter: invert(48%) sepia(89%) saturate(459%) hue-rotate(93deg) brightness(95%) contrast(92%);
  291. opacity: 1;
  292. }
  293. /* Orange/red-colored icon for heating indicators */
  294. .icon-heating {
  295. filter: invert(50%) sepia(100%) saturate(1000%) hue-rotate(360deg) brightness(100%) contrast(100%);
  296. opacity: 1;
  297. }
  298. /* Jogpad theme styling - darken background in dark mode */
  299. .jogpad-theme {
  300. /* Light mode - normal */
  301. }
  302. .dark .jogpad-theme {
  303. filter: brightness(0.35) contrast(1.2);
  304. }
  305. /* Empty AMS slot with diagonal stripes */
  306. .ams-empty-slot {
  307. background: repeating-linear-gradient(
  308. 45deg,
  309. #444,
  310. #444 2px,
  311. #222 2px,
  312. #222 4px
  313. );
  314. }
  315. .dark .ams-empty-slot {
  316. background: repeating-linear-gradient(
  317. 45deg,
  318. #555,
  319. #555 2px,
  320. #333 2px,
  321. #333 4px
  322. );
  323. }
  324. /* Touch manipulation to prevent zoom on double-tap */
  325. .touch-manipulation {
  326. touch-action: manipulation;
  327. }
  328. /* Safe area insets for notched devices */
  329. .safe-area-bottom {
  330. padding-bottom: env(safe-area-inset-bottom, 0);
  331. }
  332. .safe-area-top {
  333. padding-top: env(safe-area-inset-top, 0);
  334. }
  335. /* Hide scrollbar but keep functionality */
  336. .scrollbar-hide {
  337. -ms-overflow-style: none;
  338. scrollbar-width: none;
  339. }
  340. .scrollbar-hide::-webkit-scrollbar {
  341. display: none;
  342. }
  343. /* Mobile drawer animation */
  344. @keyframes slide-in-left {
  345. from {
  346. transform: translateX(-100%);
  347. }
  348. to {
  349. transform: translateX(0);
  350. }
  351. }
  352. .animate-slide-in-left {
  353. animation: slide-in-left 0.3s ease-out;
  354. }
  355. /* SpoolBuddy idle scan animation: transform/opacity only for low-power devices */
  356. @keyframes spoolbuddy-optimized-ping {
  357. 0% {
  358. transform: scale(0.8);
  359. opacity: 0.8;
  360. }
  361. 80%,
  362. 100% {
  363. transform: scale(2.2);
  364. opacity: 0;
  365. }
  366. }
  367. .spoolbuddy-optimized-ping {
  368. will-change: auto;
  369. contain: layout;
  370. animation: spoolbuddy-optimized-ping 3.5s cubic-bezier(0, 0, 0.2, 1) infinite;
  371. }
  372. .spoolbuddy-spool-glow {
  373. transform: scale(2.0);
  374. will-change: auto;
  375. }
  376. @media (prefers-reduced-motion: reduce) {
  377. .spoolbuddy-optimized-ping {
  378. animation: none;
  379. opacity: 0.25;
  380. transform: scale(1.2);
  381. }
  382. .spoolbuddy-spool-glow {
  383. transition: none;
  384. }
  385. }
  386. /* SpoolBuddy quick menu slide-down */
  387. @keyframes slide-down {
  388. from {
  389. transform: translateY(-100%);
  390. }
  391. to {
  392. transform: translateY(0);
  393. }
  394. }
  395. .animate-slide-down {
  396. animation: slide-down 0.25s ease-out;
  397. }
  398. /* Card shadows - uses theme-specific shadow */
  399. .card-shadow {
  400. box-shadow: var(--card-shadow);
  401. }
  402. /* ============================================
  403. SPOOLBUDDY KIOSK MODAL CONSTRAINTS
  404. Cap large modals (max-w-2xl) to viewport on the
  405. small SpoolBuddy touchscreen. Excludes smaller
  406. modals like the slot action picker (max-w-sm).
  407. ============================================ */
  408. [data-spoolbuddy-kiosk] .fixed .relative.max-w-2xl {
  409. height: 90vh;
  410. max-height: 90vh;
  411. display: flex;
  412. flex-direction: column;
  413. }
  414. [data-spoolbuddy-kiosk] .fixed .relative.max-w-2xl > div:first-child {
  415. flex-shrink: 0;
  416. }
  417. [data-spoolbuddy-kiosk] .fixed .relative.max-w-2xl > div:last-child {
  418. flex-shrink: 0;
  419. }
  420. [data-spoolbuddy-kiosk] .fixed .relative.max-w-2xl > div:nth-child(2) {
  421. flex: 1 1 auto;
  422. min-height: 0;
  423. overflow-y: auto;
  424. }
  425. /* Remove inner spool grid max-height to avoid nested scrollbars */
  426. [data-spoolbuddy-kiosk] .fixed .relative.max-w-2xl .max-h-96 {
  427. max-height: none;
  428. }
  429. /* Calendar selected-day list scrollbar theming */
  430. .calendar-scroll {
  431. scrollbar-width: thin;
  432. scrollbar-color: color-mix(in srgb, var(--text-muted) 60%, transparent) transparent;
  433. }
  434. .calendar-scroll::-webkit-scrollbar {
  435. width: 8px;
  436. }
  437. .calendar-scroll::-webkit-scrollbar-track {
  438. background: transparent;
  439. }
  440. .calendar-scroll::-webkit-scrollbar-thumb {
  441. background-color: color-mix(in srgb, var(--text-muted) 60%, transparent);
  442. border-radius: 999px;
  443. border: 2px solid color-mix(in srgb, var(--bg-secondary) 70%, transparent);
  444. }
  445. .calendar-scroll::-webkit-scrollbar-thumb:hover {
  446. background-color: color-mix(in srgb, var(--text-muted) 80%, transparent);
  447. }
  448. /* History row thumbnail hover-to-enlarge — desktop only (no hover on touch). */
  449. @media (hover: hover) and (pointer: fine) {
  450. .history-thumb-hover:hover .history-thumb-preview {
  451. opacity: 1;
  452. }
  453. }