30 CSS Grid Layouts30 / 30

Pure CSSMIT licensed

Periodic Table

The final boss of grid placement: a real 18-column periodic table where hydrogen sits at 1/1, helium jumps to column 18, and the d-block gap appears because elements are PLACED, not flowed. A working sample of periods 1–4 with category coloring — the demo that proves you understand grid coordinates.

Published

Live Demo
Try it

The code

<section class="cgl-30" aria-label="Periodic table grid demo">
  <div class="cgl-30__scroll">
    <div class="cgl-30__table" role="group" aria-label="Periodic table, periods 1 to 4 sample">
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>1</i><b>H</b><span>hydrogen</span></button>
      <button type="button" style="--c:oklch(0.75 0.12 300);grid-column:18"><i>2</i><b>He</b><span>helium</span></button>
      <button type="button" style="--c:oklch(0.72 0.16 25)"><i>3</i><b>Li</b><span>lithium</span></button>
      <button type="button" style="--c:oklch(0.78 0.13 85)"><i>4</i><b>Be</b><span>beryllium</span></button>
      <button type="button" style="--c:oklch(0.74 0.12 200);grid-column:13"><i>5</i><b>B</b><span>boron</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>6</i><b>C</b><span>carbon</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>7</i><b>N</b><span>nitrogen</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>8</i><b>O</b><span>oxygen</span></button>
      <button type="button" style="--c:oklch(0.7 0.15 350)"><i>9</i><b>F</b><span>fluorine</span></button>
      <button type="button" style="--c:oklch(0.75 0.12 300)"><i>10</i><b>Ne</b><span>neon</span></button>
      <button type="button" style="--c:oklch(0.72 0.16 25)"><i>11</i><b>Na</b><span>sodium</span></button>
      <button type="button" style="--c:oklch(0.78 0.13 85)"><i>12</i><b>Mg</b><span>magnesium</span></button>
      <button type="button" style="--c:oklch(0.74 0.12 200);grid-column:13"><i>13</i><b>Al</b><span>aluminium</span></button>
      <button type="button" style="--c:oklch(0.74 0.12 200)"><i>14</i><b>Si</b><span>silicon</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>15</i><b>P</b><span>phosphorus</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>16</i><b>S</b><span>sulfur</span></button>
      <button type="button" style="--c:oklch(0.7 0.15 350)"><i>17</i><b>Cl</b><span>chlorine</span></button>
      <button type="button" style="--c:oklch(0.75 0.12 300)"><i>18</i><b>Ar</b><span>argon</span></button>
      <button type="button" style="--c:oklch(0.72 0.16 25)"><i>19</i><b>K</b><span>potassium</span></button>
      <button type="button" style="--c:oklch(0.78 0.13 85)"><i>20</i><b>Ca</b><span>calcium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>21</i><b>Sc</b><span>scandium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>22</i><b>Ti</b><span>titanium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>23</i><b>V</b><span>vanadium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>24</i><b>Cr</b><span>chromium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>25</i><b>Mn</b><span>manganese</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>26</i><b>Fe</b><span>iron</span></button>
    </div>
  </div>
  <p class="cgl-30__note">Periods 1–4 (partial). He and B/Al are explicitly placed; auto-flow draws the rest of the silhouette.</p>
</section>
.cgl-30,
.cgl-30 *,
.cgl-30 *::before,
.cgl-30 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-30 {
  width: 100%;
  min-height: 100vh;
  --bg: oklch(0.15 0.015 260);
  font-family: system-ui,sans-serif;
  background: var(--bg);
  padding: 1.4rem;
}

.cgl-30 .cgl-30__scroll {
  overflow-x: auto;
  padding-block-end: 6px;
}

.cgl-30 .cgl-30__table {
  display: grid;
  grid-template-columns: repeat(18,minmax(30px,1fr));
  gap: 4px;
  min-inline-size: 620px;
}

.cgl-30 .cgl-30__table button {
  aspect-ratio: 1/1.05;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 3px 4px;
  border-radius: 6px;
  cursor: pointer;
  background: color-mix(in oklab,var(--c) 16%,var(--bg));
  border: 1px solid color-mix(in oklab,var(--c) 40%,var(--bg));
  transition: background .2s ease,translate .2s ease;
}

.cgl-30 .cgl-30__table button:hover {
  background: color-mix(in oklab,var(--c) 32%,var(--bg));
  translate: 0 -2px;
}

.cgl-30 .cgl-30__table button:focus-visible {
  outline: 2px solid var(--c);
  outline-offset: 2px;
}

.cgl-30 .cgl-30__table i {
  font-style: normal;
  font-size: .5rem;
  justify-self: start;
  color: color-mix(in oklab,var(--c) 75%,white);
  font-variant-numeric: tabular-nums;
}

.cgl-30 .cgl-30__table b {
  font-size: clamp(.7rem,2.2vw,.95rem);
  color: white;
  place-self: center;
}

.cgl-30 .cgl-30__table span {
  font-size: .42rem;
  letter-spacing: .02em;
  color: color-mix(in oklab,var(--c) 60%,white);
  justify-self: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-inline-size: 100%;
}

.cgl-30 .cgl-30__note {
  font-size: .7rem;
  color: oklch(0.62 0.02 260);
  margin-block-start: .8rem;
}

@media (prefers-reduced-motion: reduce) {
  .cgl-30 .cgl-30__table button {
    transition: none;
  }
}
/* No JavaScript — two explicit grid-column placements per period + forward-only auto-flow draw the table's iconic silhouette. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Grid Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Periodic Table
Source: https://codefronts.com/layouts/css-grid-layouts/periodic-table/

The final boss of grid placement: a real 18-column periodic table where hydrogen sits at 1/1, helium jumps to column 18, and the d-block gap appears because elements are PLACED, not flowed. A working sample of periods 1–4 with category coloring — the demo that proves you understand grid coordinates.
## HTML
```html
<section class="cgl-30" aria-label="Periodic table grid demo">
  <div class="cgl-30__scroll">
    <div class="cgl-30__table" role="group" aria-label="Periodic table, periods 1 to 4 sample">
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>1</i><b>H</b><span>hydrogen</span></button>
      <button type="button" style="--c:oklch(0.75 0.12 300);grid-column:18"><i>2</i><b>He</b><span>helium</span></button>
      <button type="button" style="--c:oklch(0.72 0.16 25)"><i>3</i><b>Li</b><span>lithium</span></button>
      <button type="button" style="--c:oklch(0.78 0.13 85)"><i>4</i><b>Be</b><span>beryllium</span></button>
      <button type="button" style="--c:oklch(0.74 0.12 200);grid-column:13"><i>5</i><b>B</b><span>boron</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>6</i><b>C</b><span>carbon</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>7</i><b>N</b><span>nitrogen</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>8</i><b>O</b><span>oxygen</span></button>
      <button type="button" style="--c:oklch(0.7 0.15 350)"><i>9</i><b>F</b><span>fluorine</span></button>
      <button type="button" style="--c:oklch(0.75 0.12 300)"><i>10</i><b>Ne</b><span>neon</span></button>
      <button type="button" style="--c:oklch(0.72 0.16 25)"><i>11</i><b>Na</b><span>sodium</span></button>
      <button type="button" style="--c:oklch(0.78 0.13 85)"><i>12</i><b>Mg</b><span>magnesium</span></button>
      <button type="button" style="--c:oklch(0.74 0.12 200);grid-column:13"><i>13</i><b>Al</b><span>aluminium</span></button>
      <button type="button" style="--c:oklch(0.74 0.12 200)"><i>14</i><b>Si</b><span>silicon</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>15</i><b>P</b><span>phosphorus</span></button>
      <button type="button" style="--c:oklch(0.75 0.14 150)"><i>16</i><b>S</b><span>sulfur</span></button>
      <button type="button" style="--c:oklch(0.7 0.15 350)"><i>17</i><b>Cl</b><span>chlorine</span></button>
      <button type="button" style="--c:oklch(0.75 0.12 300)"><i>18</i><b>Ar</b><span>argon</span></button>
      <button type="button" style="--c:oklch(0.72 0.16 25)"><i>19</i><b>K</b><span>potassium</span></button>
      <button type="button" style="--c:oklch(0.78 0.13 85)"><i>20</i><b>Ca</b><span>calcium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>21</i><b>Sc</b><span>scandium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>22</i><b>Ti</b><span>titanium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>23</i><b>V</b><span>vanadium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>24</i><b>Cr</b><span>chromium</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>25</i><b>Mn</b><span>manganese</span></button>
      <button type="button" style="--c:oklch(0.76 0.1 240)"><i>26</i><b>Fe</b><span>iron</span></button>
    </div>
  </div>
  <p class="cgl-30__note">Periods 1–4 (partial). He and B/Al are explicitly placed; auto-flow draws the rest of the silhouette.</p>
</section>
```
## CSS
```css
.cgl-30,
.cgl-30 *,
.cgl-30 *::before,
.cgl-30 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-30 {
  width: 100%;
  min-height: 100vh;
  --bg: oklch(0.15 0.015 260);
  font-family: system-ui,sans-serif;
  background: var(--bg);
  padding: 1.4rem;
}

.cgl-30 .cgl-30__scroll {
  overflow-x: auto;
  padding-block-end: 6px;
}

.cgl-30 .cgl-30__table {
  display: grid;
  grid-template-columns: repeat(18,minmax(30px,1fr));
  gap: 4px;
  min-inline-size: 620px;
}

.cgl-30 .cgl-30__table button {
  aspect-ratio: 1/1.05;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 3px 4px;
  border-radius: 6px;
  cursor: pointer;
  background: color-mix(in oklab,var(--c) 16%,var(--bg));
  border: 1px solid color-mix(in oklab,var(--c) 40%,var(--bg));
  transition: background .2s ease,translate .2s ease;
}

.cgl-30 .cgl-30__table button:hover {
  background: color-mix(in oklab,var(--c) 32%,var(--bg));
  translate: 0 -2px;
}

.cgl-30 .cgl-30__table button:focus-visible {
  outline: 2px solid var(--c);
  outline-offset: 2px;
}

.cgl-30 .cgl-30__table i {
  font-style: normal;
  font-size: .5rem;
  justify-self: start;
  color: color-mix(in oklab,var(--c) 75%,white);
  font-variant-numeric: tabular-nums;
}

.cgl-30 .cgl-30__table b {
  font-size: clamp(.7rem,2.2vw,.95rem);
  color: white;
  place-self: center;
}

.cgl-30 .cgl-30__table span {
  font-size: .42rem;
  letter-spacing: .02em;
  color: color-mix(in oklab,var(--c) 60%,white);
  justify-self: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-inline-size: 100%;
}

.cgl-30 .cgl-30__note {
  font-size: .7rem;
  color: oklch(0.62 0.02 260);
  margin-block-start: .8rem;
}

@media (prefers-reduced-motion: reduce) {
  .cgl-30 .cgl-30__table button {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — two explicit grid-column placements per period + forward-only auto-flow draw the table's iconic silhouette. */
```

How this works

The table is grid-template-columns: repeat(18, minmax(0,1fr)). Most elements auto-flow, but the shape of the table comes from a handful of explicit jumps: helium declares grid-column: 18, beryllium's neighbor boron declares grid-column: 13, and auto-placement's cursor (which never backtracks) flows the rest correctly behind them. Two placed elements per period draw the entire iconic silhouette.

Each element is a <button> mini-grid (number top-left, symbol centered, name below) with category hue via one --c custom property feeding background, border and hover glow through color-mix. Cells stay square-ish with aspect-ratio and readable at small sizes with container-relative font sizing. This placement approach generalizes to seating charts, calendars, game boards — anything with true coordinates.

Make it yours

  • Complete the table: keep the same two-placements-per-period recipe through period 7 + f-block rows.
  • Category filter with :has(): a checked legend checkbox dims non-matching categories — no JS.
  • Tooltips: reuse the pin-label pattern from demo 28 on each element button.
  • Shrink to a mini-map (font-size: 0 on names) for element pickers in science UIs.

Gotchas — read before shipping

  • Auto-placement's cursor only moves FORWARD — order your HTML by atomic number and the flow works; scramble it and elements pile into wrong periods.
  • 18 columns need min-width discipline: below ~560px switch to a scroll container (included) rather than crushing cells.
  • Don't encode category by color alone — the demo pairs hue with the group label line for WCAG 1.4.1.

Browser support

ChromeSafariFirefoxEdge
111+16.2+113+111+

Floor set by oklch(), color-mix() as this demo is written. The core technique itself goes back further — Chrome 57+.

Pure coordinate placement — original grid spec. The color-mix theming needs 2023 evergreens; swap for flat colors otherwise.

Techniques used in this demo

Search CodeFronts

Loading…