I created this project to familiarize myself with PUG and Sass and had a ton of fun doing it. CSS only.
A Pen by Flavio W. Brasil on CodePen.
| - var categories = ['data','prelude','core','extension'] | |
| // ELEMENT MIXIN TEMPLATE - Updated to include GitHub URL | |
| mixin element(number, symbol, name, description, weight, material, column, row, githubPath) | |
| .element(class=material)(class='c'+column)(class='r'+row)(data-github-path=githubPath) | |
| input.activate(type='radio', name='elements') | |
| input.deactivate(type='radio', name='elements') | |
| .overlay | |
| .square | |
| .model | |
| each item in weight.reverse() | |
| .orbital | |
| - for (var i = 0; i < item; i++) | |
| .electron | |
| .atomic-number= number | |
| .label | |
| .symbol= symbol | |
| .name= name | |
| .description= description | |
| ul.atomic-weight | |
| each item in weight.reverse() | |
| li= item | |
| // PLACEHOLDER MIXIN TEMPLATE | |
| mixin placeholder(number, material, column, row) | |
| .placeholder(class=material)(class='c'+column)(class='r'+row) | |
| .square= number | |
| .wrapper | |
| h1.main-title The Elements of Kyo | |
| each item in categories | |
| input.category-toggle(type='radio', id=item, name='categoxries') | |
| input.category-cancel(type='radio', id='cancel' name='categories') | |
| .periodic-table | |
| // Column 1 (rows 2-7 from top to bottom): Render, Frame, SafeClassTag, Tag, TypeMap, Record | |
| +element(1, 'Rd', 'Render', 'Safe string representation', [1], 'data', 1, 2, 'kyo-data/shared/src/main/scala/kyo/Render.scala') | |
| +element(7, 'Fr', 'Frame', 'Caller introspection', [2,5], 'data', 1, 3, 'kyo-data/shared/src/main/scala/kyo/Frame.scala') | |
| +element(13, 'Sc', 'SafeClassTag', 'Class-based tags', [2,8,3], 'data', 1, 4, 'kyo-data/shared/src/main/scala/kyo/SafeClassTag.scala') | |
| +element(19, 'Tg', 'Tag', 'Generic type tags', [2,8,8,1], 'data', 1, 5, 'kyo-data/shared/src/main/scala/kyo/Tag.scala') | |
| +element(25, 'Tm', 'TypeMap', 'Heterogeneous maps', [2,8,13,2], 'data', 1, 6, 'kyo-data/shared/src/main/scala/kyo/TypeMap.scala') | |
| +element(31, 'Rc', 'Record', 'Polymorphic records', [2,8,18,3], 'data', 1, 7, 'kyo-data/shared/src/main/scala/kyo/Record.scala') | |
| // Column 2 (rows 3-7): Maybe, Result, Chunk, KArray, Text | |
| +element(2, 'Mb', 'Maybe', 'Unboxed optional values', [2], 'data', 2, 3, 'kyo-data/shared/src/main/scala/kyo/Maybe.scala') | |
| +element(8, 'Re', 'Result', 'Unboxed error handling', [2,6], 'data', 2, 4, 'kyo-data/shared/src/main/scala/kyo/Result.scala') | |
| +element(14, 'Ch', 'Chunk', 'Optimized sequences', [2,8,4], 'data', 2, 5, 'kyo-data/shared/src/main/scala/kyo/Chunk.scala') | |
| +element(20, 'Ka', 'KArray', 'Optimized immutable array', [2,8,8,2], 'data', 2, 6, 'kyo-data/shared/src/main/scala/kyo/KArray.scala') | |
| +element(26, 'Tx', 'Text', 'Efficient string manipulation', [2,8,14,2], 'data', 2, 7, 'kyo-data/shared/src/main/scala/kyo/Text.scala') | |
| // Column 3 (rows 2, 4-7): Ansi, Instant, Duration, Schedule | |
| +element(3, 'As', 'Ansi', 'Console pretty printing', [3], 'data', 3, 4, 'kyo-data/shared/src/main/scala/kyo/Ansi.scala') | |
| +element(9, 'Is', 'Instant', 'Time representation', [2,7], 'data', 3, 5, 'kyo-data/shared/src/main/scala/kyo/Instant.scala') | |
| +element(15, 'Dr', 'Duration', 'Time intervals', [2,8,5], 'data', 3, 6, 'kyo-data/shared/src/main/scala/kyo/Duration.scala') | |
| +element(21, 'Sc', 'Schedule', 'Time-based scheduling', [2,8,9,2], 'data', 3, 7, 'kyo-data/shared/src/main/scala/kyo/Schedule.scala') | |
| // Column 4 (rows 3-6): Stream, Sink, Emit, Poll | |
| +element(4, 'St', 'Stream', 'Lazy sequence processing', [4], 'prelude', 4, 4, 'kyo-prelude/shared/src/main/scala/kyo/Stream.scala') | |
| +element(10, 'Sk', 'Sink', 'Stream consumption patterns', [2,8], 'prelude', 4, 5, 'kyo-prelude/shared/src/main/scala/kyo/Sink.scala') | |
| +element(16, 'Em', 'Emit', 'Push-based streaming', [2,8,6], 'prelude', 4, 6, 'kyo-prelude/shared/src/main/scala/kyo/Emit.scala') | |
| +element(22, 'Pl', 'Poll', 'Pull-based streaming', [2,8,10,2], 'prelude', 4, 7, 'kyo-prelude/shared/src/main/scala/kyo/Poll.scala') | |
| // Column 5 (rows 3-6): Env, Layer, Local, Var | |
| +element(5, 'En', 'Env', 'Dependency injection', [5], 'prelude', 5, 4, 'kyo-prelude/shared/src/main/scala/kyo/Env.scala') | |
| +element(11, 'Ly', 'Layer', 'Composable dependencies', [2,8,1], 'prelude', 5, 5, 'kyo-prelude/shared/src/main/scala/kyo/Layer.scala') | |
| +element(17, 'Lc', 'Local', 'Scoped context values', [2,8,7], 'prelude', 5, 6, 'kyo-prelude/shared/src/main/scala/kyo/Local.scala') | |
| +element(23, 'Vr', 'Var', 'State management', [2,8,11,2], 'prelude', 5, 7, 'kyo-prelude/shared/src/main/scala/kyo/Var.scala') | |
| // Column 6 (rows 3-6): Memo, Aspect, Parse, Batch | |
| +element(6, 'Mm', 'Memo', 'Value caching', [6], 'prelude', 6, 4, 'kyo-prelude/shared/src/main/scala/kyo/Memo.scala') | |
| +element(12, 'As', 'Aspect', 'Cross-cutting concerns', [2,8,2], 'prelude', 6, 5, 'kyo-prelude/shared/src/main/scala/kyo/Aspect.scala') | |
| +element(18, 'Pr', 'Parse', 'Text parsing combinators', [2,8,8], 'prelude', 6, 6, 'kyo-prelude/shared/src/main/scala/kyo/Parse.scala') | |
| +element(24, 'Bt', 'Batch', 'Automatic operation batching', [2,8,12,2], 'prelude', 6, 7, 'kyo-prelude/shared/src/main/scala/kyo/Batch.scala') | |
| // Column 7 (rows 2-7): IO, Resource, Abort, Debug, Check, Choice | |
| +element(27, 'IO', 'IO', 'Side effect suspension', [2,8,15,2], 'core', 6, 3, 'kyo-core/shared/src/main/scala/kyo/IO.scala') | |
| +element(32, 'Rs', 'Resource', 'Cleanup guarantees', [2,8,18,4], 'core', 7, 3, 'kyo-core/shared/src/main/scala/kyo/Resource.scala') | |
| +element(37, 'Ab', 'Abort', 'Typed error handling', [2,8,18,9], 'prelude', 7, 4, 'kyo-prelude/shared/src/main/scala/kyo/Abort.scala') | |
| +element(42, 'Db', 'Debug', 'Execution tracing', [2,8,18,14], 'prelude', 7, 5, 'kyo-prelude/shared/src/main/scala/kyo/Debug.scala') | |
| +element(47, 'Ck', 'Check', 'Runtime assertions', [2,8,18,19], 'prelude', 7, 6, 'kyo-prelude/shared/src/main/scala/kyo/Check.scala') | |
| +element(52, 'Ch', 'Choice', 'Non-deterministic branching', [2,8,18,24], 'prelude', 7, 7, 'kyo-prelude/shared/src/main/scala/kyo/Choice.scala') | |
| // Column 8 (rows 2-7): Console, Clock, Log, Random, Adder, Atomic | |
| +element(28, 'Cn', 'Console', 'Terminal I/O', [2,8,16,2], 'core', 8, 2, 'kyo-core/shared/src/main/scala/kyo/Console.scala') | |
| +element(33, 'Cl', 'Clock', 'Time operations', [2,8,18,5], 'core', 8, 3, 'kyo-core/shared/src/main/scala/kyo/Clock.scala') | |
| +element(38, 'Lg', 'Log', 'Convenient logging', [2,8,18,10], 'core', 8, 4, 'kyo-core/shared/src/main/scala/kyo/Log.scala') | |
| +element(43, 'Rn', 'Random', 'Value generation', [2,8,18,15], 'core', 8, 5, 'kyo-core/shared/src/main/scala/kyo/Random.scala') | |
| +element(48, 'Ad', 'Adder', 'Concurrent accumulation', [2,8,18,20], 'core', 8, 6, 'kyo-core/shared/src/main/scala/kyo/Adder.scala') | |
| +element(53, 'At', 'Atomic', 'Thread-safe state', [2,8,18,25], 'core', 8, 7, 'kyo-core/shared/src/main/scala/kyo/Atomic.scala') | |
| // Column 9 (rows 2-7): Async, Retry, Actor, Signal, Fiber, Admission | |
| +element(29, 'As', 'Async', 'Concurrent execution', [2,8,17,2], 'core', 9, 2, 'kyo-core/shared/src/main/scala/kyo/Async.scala') | |
| +element(34, 'Rt', 'Retry', 'Failure recovery', [2,8,18,6], 'core', 9, 3, 'kyo-core/shared/src/main/scala/kyo/Retry.scala') | |
| +element(39, 'Ac', 'Actor', 'Message passing', [2,8,18,11], 'core', 9, 4, 'kyo-core/shared/src/main/scala/kyo/Actor.scala') | |
| +element(44, 'Sg', 'Signal', 'Reactive values', [2,8,18,16], 'core', 9, 5, 'kyo-core/shared/src/main/scala/kyo/Signal.scala') | |
| +element(49, 'Fb', 'Fiber', 'Async primitives', [2,8,18,21], 'core', 9, 6, 'kyo-core/shared/src/main/scala/kyo/Fiber.scala') | |
| +element(54, 'Am', 'Admission', 'Load shedding', [2,8,18,26], 'core', 9, 7, 'kyo-core/shared/src/main/scala/kyo/Admission.scala') | |
| // Column 10 (rows 2-7): Channel, Queue, Meter, Latch, Barrier, Hub | |
| +element(30, 'Ch', 'Channel', 'Fiber communication', [2,8,18,2], 'core', 10, 2, 'kyo-core/shared/src/main/scala/kyo/Channel.scala') | |
| +element(35, 'Qu', 'Queue', 'Concurrent buffer', [2,8,18,7], 'core', 10, 3, 'kyo-core/shared/src/main/scala/kyo/Queue.scala') | |
| +element(40, 'Mt', 'Meter', 'Semaphores and rate limiting', [2,8,18,12], 'core', 10, 4, 'kyo-core/shared/src/main/scala/kyo/Meter.scala') | |
| +element(45, 'Lt', 'Latch', 'Countdown synchronization', [2,8,18,17], 'core', 10, 5, 'kyo-core/shared/src/main/scala/kyo/Latch.scala') | |
| +element(50, 'Br', 'Barrier', 'Rendezvous synchronization', [2,8,18,22], 'core', 10, 6, 'kyo-core/shared/src/main/scala/kyo/Barrier.scala') | |
| +element(55, 'Hb', 'Hub', 'Broadcast messaging', [2,8,18,27], 'core', 10, 7, 'kyo-core/shared/src/main/scala/kyo/Hub.scala') | |
| // Column 11 (rows 1-7): System, Path, Stat, Cache, Memory, Topic, Resolvers | |
| +element(56, 'Sy', 'System', 'Environment access', [2,8,18,28], 'core', 11, 1, 'kyo-core/shared/src/main/scala/kyo/System.scala') | |
| +element(36, 'Ph', 'Path', 'File handling', [2,8,18,8], 'core', 11, 2, 'kyo-core/shared/src/main/scala/kyo/Path.scala') | |
| +element(41, 'St', 'Stat', 'Metrics collection', [2,8,18,13], 'core', 11, 3, 'kyo-core/shared/src/main/scala/kyo/Stat.scala') | |
| +element(46, 'Ca', 'Cache', 'Caching via Caffeine', [2,8,18,18], 'extension', 11, 4, 'kyo-cache/shared/src/main/scala/kyo/Cache.scala') | |
| +element(51, 'Mm', 'Memory', 'Offheap memory access', [2,8,18,23], 'extension', 11, 5, 'kyo-memory/shared/src/main/scala/kyo/Memory.scala') | |
| +element(57, 'Tp', 'Topic', 'IPC/UDP via Aeron', [2,8,18,29], 'extension', 11, 6, 'kyo-topic/shared/src/main/scala/kyo/Topic.scala') | |
| +element(62, 'Rv', 'Resolvers', 'GraphQL via Caliban', [2,8,18,32,2], 'extension', 11, 7, 'kyo-resolvers/shared/src/main/scala/kyo/Resolvers.scala') | |
| // Column 12 (rows 1-7): Direct, Requests, Routes, ZIOs, Cats, Browser, STM | |
| +element(63, 'Di', 'Direct', 'Direct syntax via dotty-cps-async', [2,8,18,32,3], 'extension', 12, 1, 'kyo-direct/shared/src/main/scala/kyo/Direct.scala') | |
| +element(58, 'Rq', 'Requests', 'Http client via Sttp', [2,8,18,30], 'extension', 12, 2, 'kyo-requests/shared/src/main/scala/kyo/Requests.scala') | |
| +element(59, 'Ro', 'Routes', 'Http server via Tapir', [2,8,18,31], 'extension', 12, 3, 'kyo-routes/shared/src/main/scala/kyo/Routes.scala') | |
| +element(60, 'Zi', 'ZIOs', 'Integration with ZIO', [2,8,18,32], 'extension', 12, 4, 'kyo-zios/shared/src/main/scala/kyo/ZIOs.scala') | |
| +element(61, 'Ct', 'Cats', 'Integration with cats-effect', [2,8,18,32,1], 'extension', 12, 5, 'kyo-cats/shared/src/main/scala/kyo/Cats.scala') | |
| +element(64, 'Bw', 'Browser', 'Integration with Playwright', [2,8,18,32,4], 'extension', 12, 6, 'kyo-browser/shared/src/main/scala/kyo/Browser.scala') | |
| +element(65, 'SM', 'STM', 'Optimistic concurrency', [2,8,18,32,5], 'extension', 12, 7, 'kyo-stm/shared/src/main/scala/kyo/STM.scala') | |
| .key | |
| .row | |
| label.data(for='data') kyo-data | |
| label.prelude(for='prelude') kyo-prelude | |
| label.core(for='core') kyo-core | |
| label.extension(for='extension') extensions | |
| // GitHub source viewer iframe | |
| .source-viewer | |
| .source-header | |
| span.source-title View Source | |
| iframe#github-frame(src="about:blank" frameborder="0") |
I created this project to familiarize myself with PUG and Sass and had a ton of fun doing it. CSS only.
A Pen by Flavio W. Brasil on CodePen.
| // Add this to CodePen's JS section | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Dynamically load Prism CSS | |
| const prismCSS = document.createElement('link'); | |
| prismCSS.rel = 'stylesheet'; | |
| prismCSS.href = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css'; | |
| document.head.appendChild(prismCSS); | |
| // Load Prism JS with dependencies | |
| const loadPrism = new Promise((resolve) => { | |
| const prismCore = document.createElement('script'); | |
| prismCore.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js'; | |
| prismCore.onload = () => { | |
| // Load Java first (Scala depends on it) | |
| const prismJava = document.createElement('script'); | |
| prismJava.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-java.min.js'; | |
| prismJava.onload = () => { | |
| // Then load Scala | |
| const prismScala = document.createElement('script'); | |
| prismScala.src = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-scala.min.js'; | |
| prismScala.onload = () => resolve(); | |
| document.head.appendChild(prismScala); | |
| }; | |
| document.head.appendChild(prismJava); | |
| }; | |
| document.head.appendChild(prismCore); | |
| }); | |
| const elements = document.querySelectorAll('.element'); | |
| const baseUrl = 'https://raw.githubusercontent.com/getkyo/kyo/main/'; | |
| const githubBaseUrl = 'https://github.com/getkyo/kyo/blob/main/'; | |
| // Create the code viewer container | |
| const codeViewer = document.createElement('div'); | |
| codeViewer.className = 'code-viewer'; | |
| codeViewer.innerHTML = ` | |
| <div class="code-header"> | |
| <span class="code-title">Loading...</span> | |
| <div class="code-actions"> | |
| <a class="code-link" href="#" target="_blank">Open in GitHub</a> | |
| <button class="code-close">×</button> | |
| </div> | |
| </div> | |
| <div class="code-content"> | |
| <pre><code class="language-scala"></code></pre> | |
| </div> | |
| `; | |
| document.body.appendChild(codeViewer); | |
| // Get references to elements | |
| const codeTitle = codeViewer.querySelector('.code-title'); | |
| const codeLink = codeViewer.querySelector('.code-link'); | |
| const codeClose = codeViewer.querySelector('.code-close'); | |
| const codeElement = codeViewer.querySelector('code'); | |
| const preElement = codeViewer.querySelector('pre'); | |
| // Style the code viewer | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| .code-viewer { | |
| position: fixed; | |
| bottom: -60vh; | |
| left: 0; | |
| right: 0; | |
| height: 60vh; | |
| background: #1a1d24; | |
| box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.5); | |
| transition: bottom 0.3s ease-in-out; | |
| z-index: 1000; | |
| display: flex; | |
| flex-direction: column; | |
| border-top: 2px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .code-viewer * { | |
| text-shadow: none !important; | |
| } | |
| .code-viewer.active { | |
| bottom: 0; | |
| } | |
| .code-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 15px 20px; | |
| background: #101318; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | |
| flex-shrink: 0; | |
| } | |
| .code-title { | |
| color: #fff; | |
| font-size: 14px; | |
| font-weight: 500; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| } | |
| .code-actions { | |
| display: flex; | |
| gap: 15px; | |
| align-items: center; | |
| } | |
| .code-link { | |
| color: #4ecdc4; | |
| text-decoration: none; | |
| font-size: 13px; | |
| transition: opacity 0.2s; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| } | |
| .code-link:hover { | |
| opacity: 0.8; | |
| } | |
| .code-close { | |
| background: none; | |
| border: none; | |
| color: #fff; | |
| font-size: 24px; | |
| cursor: pointer; | |
| padding: 0; | |
| width: 30px; | |
| height: 30px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| border-radius: 4px; | |
| transition: background-color 0.2s; | |
| } | |
| .code-close:hover { | |
| background-color: rgba(255, 255, 255, 0.1); | |
| } | |
| .code-content { | |
| flex: 1; | |
| overflow: auto; | |
| background: #282c34; | |
| } | |
| .code-content pre { | |
| margin: 0; | |
| padding: 20px; | |
| min-height: 100%; | |
| box-sizing: border-box; | |
| background: transparent !important; | |
| color: #abb2bf !important; | |
| } | |
| .code-content code { | |
| font-family: 'Fira Code', 'Consolas', 'Monaco', monospace; | |
| font-size: 13px; | |
| line-height: 1.6; | |
| background: transparent !important; | |
| color: #abb2bf !important; | |
| text-shadow: none !important; | |
| } | |
| /* Force Prism styles */ | |
| .code-content pre[class*="language-"], | |
| .code-content code[class*="language-"] { | |
| color: #abb2bf !important; | |
| background: transparent !important; | |
| text-shadow: none !important; | |
| } | |
| /* Custom scrollbar for code viewer */ | |
| .code-content::-webkit-scrollbar { | |
| width: 10px; | |
| height: 10px; | |
| } | |
| .code-content::-webkit-scrollbar-track { | |
| background: #1a1d24; | |
| } | |
| .code-content::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.2); | |
| border-radius: 5px; | |
| } | |
| .code-content::-webkit-scrollbar-thumb:hover { | |
| background: rgba(255, 255, 255, 0.3); | |
| } | |
| /* Loading state */ | |
| .code-viewer.loading .code-content::after { | |
| content: 'Loading...'; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| color: rgba(255, 255, 255, 0.5); | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| text-shadow: none !important; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| // Handle element clicks | |
| elements.forEach(element => { | |
| // Listen for the radio button change instead of click | |
| const activateRadio = element.querySelector('input.activate'); | |
| const deactivateRadio = element.querySelector('input.deactivate'); | |
| activateRadio.addEventListener('change', async function() { | |
| if (this.checked) { | |
| const githubPath = element.getAttribute('data-github-path'); | |
| const elementName = element.querySelector('.name').textContent; | |
| const elementSymbol = element.querySelector('.symbol').textContent; | |
| if (githubPath) { | |
| // Update header | |
| codeTitle.textContent = `${elementSymbol} - ${elementName}`; | |
| codeLink.href = githubBaseUrl + githubPath; | |
| // Show viewer and add loading state | |
| codeViewer.classList.add('active', 'loading'); | |
| codeElement.textContent = 'Loading...'; | |
| try { | |
| // Fetch the raw code | |
| const response = await fetch(baseUrl + githubPath); | |
| const code = await response.text(); | |
| // Update code content | |
| codeElement.textContent = code; | |
| // Wait for Prism to load, then apply syntax highlighting | |
| loadPrism.then(() => { | |
| Prism.highlightElement(codeElement); | |
| }); | |
| // Remove loading state | |
| codeViewer.classList.remove('loading'); | |
| // Scroll to top | |
| preElement.scrollTop = 0; | |
| } catch (error) { | |
| console.error('Failed to fetch code:', error); | |
| codeElement.textContent = `// Error loading file: ${error.message}\n// Please try opening directly in GitHub`; | |
| codeViewer.classList.remove('loading'); | |
| } | |
| } | |
| } | |
| }); | |
| // Listen for deactivation to close the code viewer | |
| deactivateRadio.addEventListener('change', function() { | |
| if (this.checked) { | |
| codeViewer.classList.remove('active'); | |
| } | |
| }); | |
| }); | |
| // Close button handler | |
| codeClose.addEventListener('click', function() { | |
| codeViewer.classList.remove('active'); | |
| }); | |
| // Click outside to close (optional) | |
| document.addEventListener('click', function(e) { | |
| if (!e.target.closest('.element') && !e.target.closest('.code-viewer')) { | |
| codeViewer.classList.remove('active'); | |
| } | |
| }); | |
| // Keyboard shortcut to close (ESC key) | |
| document.addEventListener('keydown', function(e) { | |
| if (e.key === 'Escape') { | |
| codeViewer.classList.remove('active'); | |
| } | |
| }); | |
| }); |
| $bgColor: #101318 | |
| $gridGap: 5px | |
| // Updated colors for Scala library categories | |
| $dataColor: #ecbe59 | |
| $preludeColor: #4ecdc4 // Changed from red to a positive teal/cyan | |
| $coreColor: #52ee61 | |
| $extensionColor: #759fff | |
| body | |
| background: $bgColor | |
| text-shadow: 0 0 0.4vw currentColor | |
| margin: 0 | |
| padding: 0 | |
| min-height: 100vh | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif | |
| display: flex | |
| align-items: center | |
| justify-content: center | |
| .wrapper | |
| position: relative | |
| overflow: hidden | |
| padding: 40px | |
| width: 90vw | |
| max-width: 1200px | |
| display: flex | |
| flex-direction: column | |
| align-items: center | |
| > input | |
| -webkit-appearance: none | |
| position: fixed | |
| left: 0 | |
| top: 0 | |
| width: 100% | |
| height: 100% | |
| visibility: hidden | |
| opacity: 0 | |
| pointer-events: none | |
| // Title styling | |
| .main-title | |
| text-align: center | |
| color: #fff | |
| font-size: 2.5rem | |
| margin: 0 0 30px 0 | |
| font-weight: 300 | |
| letter-spacing: 0.1em | |
| text-transform: uppercase | |
| opacity: 0.9 | |
| width: 100% | |
| .periodic-table | |
| display: grid | |
| grid-gap: $gridGap | |
| grid-template-columns: repeat(12, 1fr) | |
| width: 100% | |
| max-width: 900px | |
| margin: 0 auto | |
| // -------------------------------------- | |
| // ELEMENT | |
| .element | |
| position: relative | |
| font-size: 0.7rem | |
| padding-bottom: 100% | |
| cursor: pointer | |
| color: #fff | |
| transition: 500ms | |
| .overlay | |
| position: fixed | |
| z-index: 1 | |
| left: 0 | |
| right: 0 | |
| top: 0 | |
| bottom: 0 | |
| background-color: $bgColor | |
| opacity: 0 | |
| pointer-events: none | |
| transition: 500ms | |
| .square | |
| position: absolute | |
| left: 0 | |
| top: 0 | |
| width: 100% | |
| height: 100% | |
| border: 2px solid | |
| box-sizing: border-box | |
| background: $bgColor | |
| display: flex | |
| flex-direction: column | |
| justify-content: center | |
| align-items: center | |
| transition-property: transform, z-index, left, right, top, bottom | |
| transition-duration: 100ms, 0ms, 200ms, 200ms, 200ms, 200ms | |
| transition-delay: 0ms, 100ms, 0ms, 0ms, 0ms, 0ms | |
| .atomic-number | |
| position: absolute | |
| left: 0 | |
| top: 0 | |
| padding: 2px | |
| .label | |
| text-align: center | |
| transition: 200ms | |
| .symbol | |
| font-size: 1.5rem | |
| .name | |
| font-size: 0.6rem | |
| // Description (atomic-mass) - hidden in main view, shown only when expanded | |
| .description | |
| position: absolute | |
| left: 0 | |
| right: 0 | |
| bottom: 0 | |
| padding: 2px | |
| text-align: center | |
| font-size: 0.55rem | |
| line-height: 1.2 | |
| opacity: 0 | |
| display: none | |
| transition: 500ms | |
| overflow: hidden | |
| word-wrap: break-word | |
| .atomic-weight | |
| display: none !important // Add !important | |
| visibility: hidden // Add this line | |
| position: absolute | |
| right: 0 | |
| top: 0 | |
| list-style: none | |
| margin: 0 | |
| padding: 2px | |
| opacity: 0 | |
| transition: 500ms | |
| text-align: right | |
| .model | |
| display: none | |
| position: absolute | |
| left: -500% | |
| right: -500% | |
| top: -500% | |
| bottom: -500% | |
| transform: scale(0.1) | |
| .orbital | |
| position: absolute | |
| left: 0 | |
| right: 0 | |
| top: 0 | |
| bottom: 0 | |
| border: 5px solid | |
| border-radius: 50% | |
| opacity: 0.25 | |
| @for $i from 1 through 7 | |
| &:nth-child(#{$i}) | |
| margin: 10% + 5.5 * ($i - 1) | |
| animation-duration: 40s - 6 * ($i - 1) | |
| .electron | |
| position: absolute | |
| left: 0 | |
| right: 0 | |
| top: 0 | |
| bottom: 0 | |
| &::before | |
| content: "" | |
| position: absolute | |
| left: calc(50% - 0.7vw) | |
| top: -0.7vw | |
| width: 1.4vw | |
| height: 1.4vw | |
| background-color: currentColor | |
| border-radius: 50% | |
| opacity: 0.75 | |
| @for $i from 1 through 32 | |
| @for $a from 1 through $i | |
| &:nth-last-child(#{$i}):first-child ~ .electron:nth-child(#{$a}) | |
| transform: rotate((360deg/$i)*($a - 1)) | |
| input[type="radio"] | |
| -webkit-appearance: none | |
| position: absolute | |
| z-index: 2 | |
| left: 0 | |
| top: 0 | |
| width: 100% | |
| height: 100% | |
| opacity: 0 | |
| cursor: pointer | |
| outline: none | |
| &.activate | |
| &:hover ~ .square | |
| z-index: 2 | |
| transform: scale(1.35) | |
| transition-delay: 0ms | |
| outline: none | |
| pointer-events: none | |
| &:checked + input[type="radio"].deactivate | |
| z-index: 3 | |
| pointer-events: all | |
| &:checked ~ .overlay | |
| opacity: 0.75 | |
| &:checked ~ .square | |
| z-index: 3 | |
| transform: scale(3) | |
| transition-duration: 500ms, 0ms, 200ms, 200ms, 200ms, 200ms | |
| transition-delay: 0ms | |
| outline: none | |
| cursor: auto | |
| .label | |
| transition-duration: 500ms | |
| transform: scale(0.75) | |
| .atomic-weight | |
| opacity: 1 | |
| transition: 500ms | |
| display: block | |
| // Show description when expanded | |
| .description | |
| opacity: 1 | |
| display: block | |
| transition: 500ms | |
| .model | |
| display: block | |
| animation: fade-in | |
| animation-duration: 1s | |
| .orbital | |
| animation-name: rotate | |
| animation-timing-function: linear | |
| animation-iteration-count: infinite | |
| &.deactivate | |
| position: fixed | |
| display: block | |
| z-index: 1 | |
| opacity: 0 | |
| pointer-events: none | |
| &:checked ~ .square | |
| z-index: 1 | |
| // -------------------------------------- | |
| // PLACEHOLDER | |
| .placeholder | |
| position: relative | |
| z-index: -1 | |
| font-size: 1vw | |
| padding-bottom: 100% | |
| color: #fff | |
| transition: 500ms | |
| .square | |
| position: absolute | |
| left: 0 | |
| top: 0 | |
| width: 100% | |
| height: 100% | |
| border: 2px solid | |
| box-sizing: border-box | |
| display: flex | |
| flex-direction: column | |
| justify-content: center | |
| align-items: center | |
| opacity: 0.5 | |
| // -------------------------------------- | |
| // GAP | |
| .gap | |
| position: relative | |
| padding-bottom: 100% | |
| transition: 500ms | |
| &::before | |
| content: "" | |
| position: absolute | |
| left: 50% | |
| top: 0 | |
| width: 50% | |
| height: calc(200% + #{$gridGap} * 2 - 4px) | |
| border-width: 0 0 2px 2px | |
| border-style: solid | |
| margin-left: -1px | |
| color: #fff | |
| opacity: 0.2 | |
| // -------------------------------------- | |
| // COLORS - Updated for Scala library categories | |
| .data | |
| color: $dataColor | |
| .prelude | |
| color: $preludeColor | |
| .core | |
| color: $coreColor | |
| .extension | |
| color: $extensionColor | |
| // -------------------------------------- | |
| // ROWS & COLUMNS | |
| @for $i from 1 through 10 | |
| .r#{$i} | |
| grid-row: $i | |
| @for $i from 1 through 12 | |
| .c#{$i} | |
| grid-column: $i | |
| // -------------------------------------- | |
| // SHIFT EDGE ELEMENTS ON ZOOM | |
| .r1 input[type="radio"].activate:checked ~ .square | |
| top: 100% | |
| .r10 input[type="radio"].activate:checked ~ .square | |
| top: -100% | |
| .c1 input[type="radio"].activate:checked ~ .square | |
| left: 100% | |
| .c12 input[type="radio"].activate:checked ~ .square | |
| left: -100% | |
| // -------------------------------------- | |
| // ANIMATIONS | |
| @keyframes rotate | |
| from | |
| transform: rotate(0deg) | |
| to | |
| transform: rotate(360deg) | |
| @keyframes fade-in | |
| from | |
| opacity: 0 | |
| to | |
| opacity: 1 | |
| @keyframes noise | |
| 0%, 100% | |
| background-position: 0 0 | |
| 10% | |
| background-position: -5% -10% | |
| 20% | |
| background-position: -15% 5% | |
| 30% | |
| background-position: 7% -25% | |
| 40% | |
| background-position: 20% 25% | |
| 50% | |
| background-position: -25% 10% | |
| 60% | |
| background-position: 15% 5% | |
| 70% | |
| background-position: 0% 15% | |
| 80% | |
| background-position: 25% 35% | |
| 90% | |
| background-position: -10% 10% | |
| // -------------------------------------- | |
| // KEY | |
| .key | |
| position: relative | |
| z-index: 1 | |
| grid-row: 1 | |
| grid-column-start: 1 | |
| grid-column-end: 5 | |
| font-size: 0.9rem | |
| line-height: 1.5 | |
| display: flex | |
| align-items: center | |
| pointer-events: none | |
| user-select: none | |
| .row | |
| position: relative | |
| display: flex | |
| width: 100% | |
| justify-content: space-between | |
| label | |
| opacity: 0.85 | |
| cursor: pointer | |
| transition: 120ms | |
| pointer-events: all | |
| &:hover | |
| opacity: 1 !important | |
| // -------------------------------------- | |
| // CATEGORY TOGGLES - Updated for Scala library categories | |
| #data:checked ~ .periodic-table .element:not(.data), | |
| #prelude:checked ~ .periodic-table .element:not(.prelude), | |
| #core:checked ~ .periodic-table .element:not(.core), | |
| #extension:checked ~ .periodic-table .element:not(.extension), | |
| #data:checked ~ .periodic-table .placeholder, | |
| #prelude:checked ~ .periodic-table .placeholder, | |
| #core:checked ~ .periodic-table .placeholder, | |
| #extension:checked ~ .periodic-table .placeholder | |
| opacity: 0.15 | |
| pointer-events: none | |
| #data:checked ~ .periodic-table .key label:not(.data), | |
| #prelude:checked ~ .periodic-table .key label:not(.prelude), | |
| #core:checked ~ .periodic-table .key label:not(.core), | |
| #extension:checked ~ .periodic-table .key label:not(.extension) | |
| opacity: 0.65 | |
| .category-toggle:checked ~ .category-cancel | |
| visibility: visible | |
| pointer-events: all | |
| cursor: pointer |