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 |