fuzzyworld-tools / files / server / site / _shared.css
_shared.css
510a1337 2026-10-28 Pavel Safronov | 1 | /* ============================================================================= |
| 2 | FuzzyWorld Shared Design System | |
| 3 | ||
| 4 | This file contains design tokens and common base styles shared across | |
| 5 | all fuzzyworld.net sites (fuzzyworld.net, code.fuzzyworld.net, apps.fuzzyworld.net). | |
| 6 | ||
| 7 | Import this file before site-specific styles. | |
| 8 | ============================================================================= */ | |
| 9 | ||
| 10 | /* --------------------------------------------------------------------- | |
| 11 | Design Tokens: Colors | |
| 12 | --------------------------------------------------------------------- */ | |
| 13 | ||
| 14 | :root { | |
| 15 | /* Light mode (default) */ | |
| 16 | --color-bg-primary: #ffffff; | |
| 17 | --color-bg-secondary: #f6f6f8; | |
| 18 | --color-bg-tertiary: #ffffff; | |
| 19 | --color-bg-hover: #eaeaf0; | |
| 20 | ||
| 21 | --color-fg-primary: #1d1d1f; | |
| 22 | --color-fg-secondary: #62626a; | |
| 23 | --color-fg-muted: #9a9aa2; | |
| 24 | ||
| 25 | --color-border-primary: #e1e1e6; | |
| 26 | --color-border-secondary: #c1c1c7; | |
| 27 | ||
| 28 | /* Accent colors */ | |
| 29 | --color-accent: #0a58ca; | |
| 30 | --color-accent-soft: #e8f0fe; | |
| 31 | --color-accent-muted: #79b8ff; | |
| 32 | ||
| 33 | /* Semantic colors */ | |
| 34 | --color-success: #116329; | |
| 35 | --color-success-bg: #e6f4ea; | |
| 36 | --color-error: #a40e26; | |
| 37 | --color-error-bg: #fdecea; | |
| 38 | --color-warning: #8250df; | |
| 39 | --color-warning-bg: #e8e8ea; | |
| 40 | ||
| 41 | /* Code syntax colors (light) */ | |
| 42 | --color-hl-comment: #6a737d; | |
| 43 | --color-hl-keyword: #cf222e; | |
| 44 | --color-hl-string: #0a3069; | |
| 45 | --color-hl-number: #0550ae; | |
| 46 | --color-hl-function: #8250df; | |
| 47 | --color-hl-class: #953800; | |
| 48 | --color-hl-type: #116329; | |
| 49 | } | |
| 50 | ||
| 51 | /* Dark mode */ | |
| 52 | @media (prefers-color-scheme: dark) { | |
| 53 | :root { | |
| 54 | --color-bg-primary: #111113; | |
| 55 | --color-bg-secondary: #1a1a1e; | |
| 56 | --color-bg-tertiary: #17171a; | |
| 57 | --color-bg-hover: #2a2a2f; | |
| 58 | ||
| 59 | --color-fg-primary: #e8e8ea; | |
| 60 | --color-fg-secondary: #9a9aa2; | |
| 61 | --color-fg-muted: #62626a; | |
| 62 | ||
| 63 | --color-border-primary: #2a2a2f; | |
| 64 | --color-border-secondary: #3e3e42; | |
| 65 | ||
| 66 | /* Accent colors */ | |
| 67 | --color-accent: #79b8ff; | |
| 68 | --color-accent-soft: #16243a; | |
| 69 | --color-accent-muted: #bf8700; | |
| 70 | ||
| 71 | /* Semantic colors */ | |
| 72 | --color-success: #7ee787; | |
| 73 | --color-success-bg: #12261a; | |
| 74 | --color-error: #ff9492; | |
| 75 | --color-error-bg: #2d1517; | |
| 76 | --color-warning: #c5a5ff; | |
| 77 | --color-warning-bg: #2a2a2f; | |
| 78 | ||
| 79 | /* Code syntax colors (dark) */ | |
| 80 | --color-hl-comment: #8b949e; | |
| 81 | --color-hl-keyword: #ff7b72; | |
| 82 | --color-hl-string: #a5d6ff; | |
| 83 | --color-hl-number: #79c0ff; | |
| 84 | --color-hl-function: #d2a8ff; | |
| 85 | --color-hl-class: #ffa657; | |
| 86 | --color-hl-type: #7ee787; | |
| 87 | } | |
| 88 | } | |
| 89 | ||
| 90 | /* Force color scheme support */ | |
| 91 | :root { | |
| 92 | color-scheme: light dark; | |
| 93 | } | |
| 94 | ||
| 95 | /* --------------------------------------------------------------------- | |
| 96 | Design Tokens: Spacing | |
| 97 | --------------------------------------------------------------------- */ | |
| 98 | ||
| 99 | :root { | |
| 100 | --spacing-xs: 0.25rem; | |
| 101 | --spacing-sm: 0.5rem; | |
| 102 | --spacing-md: 1rem; | |
| 103 | --spacing-lg: 1.5rem; | |
| 104 | --spacing-xl: 2rem; | |
| 105 | --spacing-2xl: 3rem; | |
| 106 | ||
| 107 | /* For grid gaps */ | |
| 108 | --gap-sm: 0.75rem; | |
| 109 | --gap-md: 1rem; | |
| 110 | --gap-lg: 1.5rem; | |
| 111 | } | |
| 112 | ||
| 113 | /* --------------------------------------------------------------------- | |
| 114 | Design Tokens: Typography | |
| 115 | --------------------------------------------------------------------- */ | |
| 116 | ||
| 117 | :root { | |
| 118 | --font-family-sans: system-ui, -apple-system, "Segoe UI", sans-serif; | |
| 119 | --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; | |
| 120 | ||
| 121 | --font-size-xs: 0.8rem; | |
| 122 | --font-size-sm: 0.9rem; | |
| 123 | --font-size-base: 1rem; | |
| 124 | --font-size-lg: 1.1rem; | |
| 125 | --font-size-xl: 1.2rem; | |
| 126 | --font-size-2xl: 1.75rem; | |
| 127 | ||
| 128 | --font-weight-normal: 400; | |
| 129 | --font-weight-medium: 500; | |
| 130 | --font-weight-semibold: 600; | |
| 131 | --font-weight-bold: 700; | |
| 132 | ||
| 133 | --line-height-tight: 1.25; | |
| 134 | --line-height-normal: 1.5; | |
| 135 | --line-height-relaxed: 1.55; | |
| 136 | } | |
| 137 | ||
| 138 | /* --------------------------------------------------------------------- | |
| 139 | Design Tokens: Borders & Shadows | |
| 140 | --------------------------------------------------------------------- */ | |
| 141 | ||
| 142 | :root { | |
| 143 | --border-radius-sm: 4px; | |
| 144 | --border-radius-md: 6px; | |
| 145 | --border-radius-lg: 8px; | |
| 146 | --border-radius-full: 9999px; | |
| 147 | ||
| 148 | --box-shadow-sm: 0 1px 2px rgb(0 0 0 / 6%); | |
| 149 | --box-shadow-md: 0 4px 6px rgb(0 0 0 / 8%); | |
| 150 | ||
| 151 | /* No shadow in dark mode */ | |
| 152 | --box-shadow: var(--box-shadow-sm); | |
| 153 | } | |
| 154 | ||
| 155 | @media (prefers-color-scheme: dark) { | |
| 156 | :root { | |
| 157 | --box-shadow: none; | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | /* --------------------------------------------------------------------- | |
| 162 | Design Tokens: Transitions | |
| 163 | --------------------------------------------------------------------- */ | |
| 164 | ||
| 165 | :root { | |
| 166 | --transition-fast: 100ms ease; | |
| 167 | --transition-normal: 200ms ease; | |
| 168 | --transition-slow: 300ms ease; | |
| 169 | } | |
| 170 | ||
| 171 | /* --------------------------------------------------------------------- | |
| 172 | Base Styles | |
| 173 | --------------------------------------------------------------------- */ | |
| 174 | ||
| 175 | * { | |
| 176 | box-sizing: border-box; | |
| 177 | } | |
| 178 | ||
| 179 | html { | |
| 180 | -webkit-text-size-adjust: 100%; | |
| 181 | font-size: 16px; | |
| 182 | } | |
| 183 | ||
| 184 | body { | |
| 185 | margin: 0; | |
| 186 | background: var(--color-bg-primary); | |
| 187 | color: var(--color-fg-primary); | |
| 188 | font: var(--font-size-base)/var(--line-height-relaxed) var(--font-family-sans); | |
| 189 | -webkit-font-smoothing: antialiased; | |
| 190 | -moz-osx-font-smoothing: grayscale; | |
| 191 | } | |
| 192 | ||
| 193 | /* Text selection */ | |
| 194 | ::selection { | |
| 195 | background: var(--color-accent); | |
| 196 | color: var(--color-bg-primary); | |
| 197 | } | |
| 198 | ||
| 199 | /* Focus styles */ | |
| 200 | :focus-visible { | |
| 201 | outline: 2px solid var(--color-accent); | |
| 202 | outline-offset: 2px; | |
| 203 | border-radius: var(--border-radius-sm); | |
| 204 | } | |
| 205 | ||
| 206 | /* Links */ | |
| 207 | a { | |
| 208 | color: var(--color-accent); | |
| 209 | text-decoration: none; | |
| 210 | } | |
| 211 | ||
| 212 | a:hover { | |
| 213 | text-decoration: underline; | |
| 214 | } | |
| 215 | ||
| 216 | /* Images */ | |
| 217 | img, picture, video, canvas, svg { | |
| 218 | display: block; | |
| 219 | max-width: 100%; | |
| 220 | } | |
| 221 | ||
| 222 | /* Lists */ | |
| 223 | ul, ol { | |
| 224 | padding: 0; | |
| 225 | margin: 0; | |
| 226 | } | |
| 227 | ||
| 228 | /* Forms */ | |
| 229 | input, button, textarea, select { | |
| 230 | font: inherit; | |
| 231 | } | |
| 232 | ||
| 233 | /* Code */ | |
| 234 | code, kbd, samp, pre { | |
| 235 | font-family: var(--font-family-mono); | |
| 236 | } | |
| 237 | ||
| 238 | /* Headings */ | |
| 239 | h1, h2, h3, h4, h5, h6 { | |
| 240 | font-weight: var(--font-weight-semibold); | |
| 241 | line-height: var(--line-height-tight); | |
| 242 | letter-spacing: -0.01em; | |
| 243 | } | |
| 244 | ||
| 245 | /* --------------------------------------------------------------------- | |
| 246 | Layout Utilities | |
| 247 | --------------------------------------------------------------------- */ | |
| 248 | ||
| 249 | .main-container { | |
| 250 | max-width: 72rem; | |
| 251 | margin: 0 auto; | |
| 252 | padding: 0 var(--spacing-md) var(--spacing-2xl); | |
| 253 | } | |
| 254 | ||
| 255 | /* Visually hidden but accessible */ | |
| 256 | .visually-hidden { | |
| 257 | position: absolute; | |
| 258 | width: 1px; | |
| 259 | height: 1px; | |
| 260 | padding: 0; | |
| 261 | margin: -1px; | |
| 262 | overflow: hidden; | |
| 263 | clip: rect(0, 0, 0, 0); | |
| 264 | white-space: nowrap; | |
| 265 | border: 0; | |
| 266 | } | |
| 267 | ||
| 268 | /* Text utilities */ | |
| 269 | .text-muted { color: var(--color-fg-muted); } | |
| 270 | .text-secondary { color: var(--color-fg-secondary); } | |
| 271 | .text-success { color: var(--color-success); } | |
| 272 | .text-error { color: var(--color-error); } | |
| 273 | .text-warning { color: var(--color-warning); } | |
| 274 | ||
| 275 | /* Truncate text */ | |
| 276 | .truncate { | |
| 277 | overflow: hidden; | |
| 278 | text-overflow: ellipsis; | |
| 279 | white-space: nowrap; | |
| 280 | } | |
| 281 | ||
| 282 | /* Overflow wrap */ | |
| 283 | .overflow-wrap-anywhere { | |
| 284 | overflow-wrap: anywhere; | |
| 285 | } | |
| 286 | ||
| 287 | /* --------------------------------------------------------------------- | |
| 288 | Spacing Utilities | |
| 289 | --------------------------------------------------------------------- */ | |
| 290 | ||
| 291 | .mt-xs { margin-top: var(--spacing-xs); } | |
| 292 | .mt-sm { margin-top: var(--spacing-sm); } | |
| 293 | .mt-md { margin-top: var(--spacing-md); } | |
| 294 | .mt-lg { margin-top: var(--spacing-lg); } | |
| 295 | .mt-xl { margin-top: var(--spacing-xl); } | |
| 296 | ||
| 297 | .mb-xs { margin-bottom: var(--spacing-xs); } | |
| 298 | .mb-sm { margin-bottom: var(--spacing-sm); } | |
| 299 | .mb-md { margin-bottom: var(--spacing-md); } | |
| 300 | .mb-lg { margin-bottom: var(--spacing-lg); } | |
| 301 | .mb-xl { margin-bottom: var(--spacing-xl); } | |
| 302 | ||
| 303 | /* --------------------------------------------------------------------- | |
| 304 | Card Component | |
| 305 | --------------------------------------------------------------------- */ | |
| 306 | ||
| 307 | .card { | |
| 308 | background: var(--color-bg-tertiary); | |
| 309 | border: 1px solid var(--color-border-primary); | |
| 310 | border-radius: var(--border-radius-lg); | |
| 311 | box-shadow: var(--box-shadow); | |
| 312 | padding: var(--spacing-md); | |
| 313 | } | |
| 314 | ||
| 315 | .card:hover { | |
| 316 | border-color: var(--color-accent); | |
| 317 | } | |
| 318 | ||
| 319 | /* --------------------------------------------------------------------- | |
| 320 | Button Component | |
| 321 | --------------------------------------------------------------------- */ | |
| 322 | ||
| 323 | .btn { | |
| 324 | display: inline-flex; | |
| 325 | align-items: center; | |
| 326 | gap: var(--spacing-xs); | |
| 327 | padding: var(--spacing-xs) var(--spacing-md); | |
| 328 | background: var(--color-accent); | |
| 329 | color: var(--color-bg-primary); | |
| 330 | border: none; | |
| 331 | border-radius: var(--border-radius-md); | |
| 332 | font: inherit; | |
| 333 | font-size: var(--font-size-sm); | |
| 334 | cursor: pointer; | |
| 335 | transition: background var(--transition-fast); | |
| 336 | } | |
| 337 | ||
| 338 | .btn:hover { | |
| 339 | background: var(--color-accent-muted); | |
| 340 | } | |
| 341 | ||
| 342 | .btn-secondary { | |
| 343 | background: var(--color-bg-secondary); | |
| 344 | color: var(--color-fg-primary); | |
| 345 | border: 1px solid var(--color-border-primary); | |
| 346 | } | |
| 347 | ||
| 348 | .btn-secondary:hover { | |
| 349 | background: var(--color-bg-hover); | |
| 350 | } | |
| 351 | ||
| 352 | /* --------------------------------------------------------------------- | |
| 353 | Badge Component | |
| 354 | --------------------------------------------------------------------- */ | |
| 355 | ||
| 356 | .badge { | |
| 357 | display: inline-block; | |
| 358 | padding: var(--spacing-xs) var(--spacing-sm); | |
| 359 | background: var(--color-bg-secondary); | |
| 360 | color: var(--color-fg-secondary); | |
| 361 | border: 1px solid var(--color-border-primary); | |
| 362 | border-radius: var(--border-radius-full); | |
| 363 | font-size: var(--font-size-xs); | |
| 364 | } | |
| 365 | ||
| 366 | .badge-success { | |
| 367 | background: var(--color-success-bg); | |
| 368 | color: var(--color-success); | |
| 369 | border-color: var(--color-success); | |
| 370 | } | |
| 371 | ||
| 372 | .badge-error { | |
| 373 | background: var(--color-error-bg); | |
| 374 | color: var(--color-error); | |
| 375 | border-color: var(--color-error); | |
| 376 | } | |
| 377 | ||
| 378 | .badge-warning { | |
| 379 | background: var(--color-warning-bg); | |
| 380 | color: var(--color-warning); | |
| 381 | border-color: var(--color-warning); | |
| 382 | } | |
| 383 | ||
| 384 | /* --------------------------------------------------------------------- | |
| 385 | Table Styles | |
| 386 | --------------------------------------------------------------------- */ | |
| 387 | ||
| 388 | table { | |
| 389 | border-collapse: collapse; | |
| 390 | width: 100%; | |
| 391 | } | |
| 392 | ||
| 393 | td, th { | |
| 394 | padding: var(--spacing-xs) var(--spacing-sm); | |
| 395 | border-bottom: 1px solid var(--color-border-primary); | |
| 396 | text-align: left; | |
| 397 | vertical-align: top; | |
| 398 | } | |
| 399 | ||
| 400 | th { | |
| 401 | color: var(--color-fg-muted); | |
| 402 | font-weight: var(--font-weight-medium); | |
| 403 | font-size: var(--font-size-sm); | |
| 404 | } | |
| 405 | ||
| 406 | table:not(.source) tr:hover > td { | |
| 407 | background: var(--color-bg-secondary); | |
| 408 | } | |
| 409 | ||
| 410 | /* --------------------------------------------------------------------- | |
| 411 | Back to Top Button | |
| 412 | --------------------------------------------------------------------- */ | |
| 413 | ||
| 414 | .back-to-top { | |
| 415 | position: fixed; | |
| 416 | bottom: var(--spacing-xl); | |
| 417 | right: var(--spacing-xl); | |
| 418 | width: 2.5rem; | |
| 419 | height: 2.5rem; | |
| 420 | display: flex; | |
| 421 | align-items: center; | |
| 422 | justify-content: center; | |
| 423 | background: var(--color-bg-secondary); | |
| 424 | border: 1px solid var(--color-border-primary); | |
| 425 | border-radius: var(--border-radius-full); | |
| 426 | color: var(--color-fg-primary); | |
| 427 | font-size: 1.25rem; | |
| 428 | cursor: pointer; | |
| 429 | transition: all var(--transition-fast); | |
| 430 | opacity: 0; | |
| 431 | visibility: hidden; | |
| 432 | z-index: 1000; | |
| 433 | } | |
| 434 | ||
| 435 | .back-to-top:hover { | |
| 436 | background: var(--color-bg-hover); | |
| 437 | } | |
| 438 | ||
| 439 | .back-to-top.visible { | |
| 440 | opacity: 1; | |
| 441 | visibility: visible; | |
| 442 | } | |
| 443 | ||
| 444 | /* --------------------------------------------------------------------- | |
| 445 | Responsive Utilities | |
| 446 | --------------------------------------------------------------------- */ | |
| 447 | ||
| 448 | @media (max-width: 64rem) { | |
| 449 | .main-container { | |
| 450 | padding: 0 var(--spacing-sm) var(--spacing-xl); | |
| 451 | } | |
| 452 | } | |
| 453 | ||
| 454 | @media (max-width: 40rem) { | |
| 455 | .hide-on-mobile { | |
| 456 | display: none !important; | |
| 457 | } | |
| 458 | } |