home

Periodic Table

Creator: Ivan Griffin (original)

The periodic table of elements, a graphic formulation of the periodic dependence of elemental properties on their atomic numbers. The table is divided into four roughly rectangular areas called blocks. Rows are called periods, columns are groups. Elements from the same group show similar chemical characteristics. Trends run through the periodic table, with nonmetallic character (keeping their own electrons) increasing from left to right across a period, and from down to up across a group, and metallic character (surrendering electrons to other atoms) increasing in the opposite direction.


Periodic Table

Download

PNG PDF SVG

Code

periodic-table.typ (293 lines)

#import "@preview/cetz:0.5.2": canvas, draw
#import draw: content, rect

#set page(width: auto, height: auto, margin: 15pt, fill: none)
#set text(size: 12pt)

// Element colors
#let colors = (
  alkali-metal: rgb("#8989ff"),
  alkaline-earth: rgb("#89a9ff"),
  metal: rgb("#89c9ff"),
  metalloid: rgb("#ffa959"),
  nonmetal: rgb("#59d9d9"),
  halogen: rgb("#ffff59"),
  noble-gas: rgb("#89ff89"),
  lanthanide: rgb("#ff8989"),
  synthetic: rgb("#525252"),
)

// Element data
#let elements = (
  // Period 1
  (
    (1, 1.0079, "H", "Hydrogen", colors.nonmetal),
    ..(none,) * 16,
    (2, 4.0025, "He", "Helium", colors.noble-gas),
  ),
  // Period 2
  (
    (3, 6.941, "Li", "Lithium", colors.alkali-metal),
    (4, 9.0122, "Be", "Beryllium", colors.alkaline-earth),
    ..(none,) * 10,
    (5, 10.811, "B", "Boron", colors.metalloid),
    (6, 12.011, "C", "Carbon", colors.nonmetal),
    (7, 14.007, "N", "Nitrogen", colors.nonmetal),
    (8, 15.999, "O", "Oxygen", colors.nonmetal),
    (9, 18.998, "F", "Fluorine", colors.halogen),
    (10, 20.180, "Ne", "Neon", colors.noble-gas),
  ),
  // Period 3
  (
    (11, 22.990, "Na", "Sodium", colors.alkali-metal),
    (12, 24.305, "Mg", "Magnesium", colors.alkaline-earth),
    ..(none,) * 10,
    (13, 26.982, "Al", "Aluminum", colors.metal),
    (14, 28.086, "Si", "Silicon", colors.metalloid),
    (15, 30.974, "P", "Phosphorus", colors.nonmetal),
    (16, 32.065, "S", "Sulfur", colors.nonmetal),
    (17, 35.453, "Cl", "Chlorine", colors.halogen),
    (18, 39.948, "Ar", "Argon", colors.noble-gas),
  ),
  // Period 4
  (
    (19, 39.098, "K", "Potassium", colors.alkali-metal),
    (20, 40.078, "Ca", "Calcium", colors.alkaline-earth),
    (21, 44.956, "Sc", "Scandium", colors.metal),
    (22, 47.867, "Ti", "Titanium", colors.metal),
    (23, 50.942, "V", "Vanadium", colors.metal),
    (24, 51.996, "Cr", "Chromium", colors.metal),
    (25, 54.938, "Mn", "Manganese", colors.metal),
    (26, 55.845, "Fe", "Iron", colors.metal),
    (27, 58.933, "Co", "Cobalt", colors.metal),
    (28, 58.693, "Ni", "Nickel", colors.metal),
    (29, 63.546, "Cu", "Copper", colors.metal),
    (30, 65.39, "Zn", "Zinc", colors.metal),
    (31, 69.723, "Ga", "Gallium", colors.metal),
    (32, 72.64, "Ge", "Germanium", colors.metalloid),
    (33, 74.922, "As", "Arsenic", colors.metalloid),
    (34, 78.96, "Se", "Selenium", colors.nonmetal),
    (35, 79.904, "Br", "Bromine", colors.halogen),
    (36, 83.8, "Kr", "Krypton", colors.noble-gas),
  ),
  // Period 5
  (
    (37, 85.468, "Rb", "Rubidium", colors.alkali-metal),
    (38, 87.62, "Sr", "Strontium", colors.alkaline-earth),
    (39, 88.906, "Y", "Yttrium", colors.metal),
    (40, 91.224, "Zr", "Zirconium", colors.metal),
    (41, 92.906, "Nb", "Niobium", colors.metal),
    (42, 95.94, "Mo", "Molybdenum", colors.metal),
    (43, 96, "Tc", "Technetium", colors.metal),
    (44, 101.07, "Ru", "Ruthenium", colors.metal),
    (45, 102.91, "Rh", "Rhodium", colors.metal),
    (46, 106.42, "Pd", "Palladium", colors.metal),
    (47, 107.87, "Ag", "Silver", colors.metal),
    (48, 112.41, "Cd", "Cadmium", colors.metal),
    (49, 114.82, "In", "Indium", colors.metal),
    (50, 118.71, "Sn", "Tin", colors.metal),
    (51, 121.76, "Sb", "Antimony", colors.metalloid),
    (52, 127.6, "Te", "Tellurium", colors.metalloid),
    (53, 126.9, "I", "Iodine", colors.halogen),
    (54, 131.29, "Xe", "Xenon", colors.noble-gas),
  ),
  // Period 6
  (
    (55, 132.91, "Cs", "Cesium", colors.alkali-metal),
    (56, 137.33, "Ba", "Barium", colors.alkaline-earth),
    ("57-71", "", text("La-Lu", size: 28pt), "Lanthanide", colors.lanthanide),
    (72, 178.49, "Hf", "Hafnium", colors.metal),
    (73, 180.95, "Ta", "Tantalum", colors.metal),
    (74, 183.84, "W", "Tungsten", colors.metal),
    (75, 186.21, "Re", "Rhenium", colors.metal),
    (76, 190.23, "Os", "Osmium", colors.metal),
    (77, 192.22, "Ir", "Iridium", colors.metal),
    (78, 195.08, "Pt", "Platinum", colors.metal),
    (79, 196.97, "Au", "Gold", colors.metal),
    (80, 200.59, "Hg", "Mercury", colors.metal),
    (81, 204.38, "Tl", "Thallium", colors.metal),
    (82, 207.2, "Pb", "Lead", colors.metal),
    (83, 208.98, "Bi", "Bismuth", colors.metal),
    (84, 209, "Po", "Polonium", colors.metalloid),
    (85, 210, "At", "Astatine", colors.halogen),
    (86, 222, "Rn", "Radon", colors.noble-gas),
  ),
  // Period 7
  (
    (87, 223, "Fr", "Francium", colors.alkali-metal),
    (88, 226, "Ra", "Radium", colors.alkaline-earth),
    ("89-103", "", text("Ac-Lr", size: 28pt), "Actinide", colors.lanthanide),
    (104, 261, "Rf", "Rutherfordium", colors.metal),
    (105, 262, "Db", "Dubnium", colors.metal),
    (106, 266, "Sg", "Seaborgium", colors.metal),
    (107, 264, "Bh", "Bohrium", colors.metal),
    (108, 277, "Hs", "Hassium", colors.metal),
    (109, 268, "Mt", "Meitnerium", colors.metal),
    (110, 281, "Ds", "Darmstadtium", colors.metal),
    (111, 280, "Rg", "Roentgenium", colors.metal),
    (112, 285, "Cn", "Copernicium", colors.metal),
    (113, 284, "Nh", "Nihonium", colors.metal),
    (114, 289, "Fl", "Flerovium", colors.metal),
    (115, 288, "Mc", "Moscovium", colors.metal),
    (116, 293, "Lv", "Livermorium", colors.metal),
    (117, 294, "Ts", "Tennessine", colors.halogen),
    (118, 294, "Og", "Oganesson", colors.noble-gas),
  ),
)

// Lanthanide data
#let lanthanides = (
  (57, 138.91, "La", "Lanthanum"),
  (58, 140.12, "Ce", "Cerium"),
  (59, 140.91, "Pr", "Praseodymium"),
  (60, 144.24, "Nd", "Neodymium"),
  (61, 145, "Pm", "Promethium"),
  (62, 150.36, "Sm", "Samarium"),
  (63, 151.96, "Eu", "Europium"),
  (64, 157.25, "Gd", "Gadolinium"),
  (65, 158.93, "Tb", "Terbium"),
  (66, 162.50, "Dy", "Dysprosium"),
  (67, 164.93, "Ho", "Holmium"),
  (68, 167.26, "Er", "Erbium"),
  (69, 168.93, "Tm", "Thulium"),
  (70, 173.04, "Yb", "Ytterbium"),
  (71, 174.97, "Lu", "Lutetium"),
)

// Actinide data
#let actinides = (
  (89, 227, "Ac", "Actinium"),
  (90, 232.04, "Th", "Thorium"),
  (91, 231.04, "Pa", "Protactinium"),
  (92, 238.03, "U", "Uranium"),
  (93, 237, "Np", "Neptunium"),
  (94, 244, "Pu", "Plutonium"),
  (95, 243, "Am", "Americium"),
  (96, 247, "Cm", "Curium"),
  (97, 247, "Bk", "Berkelium"),
  (98, 251, "Cf", "Californium"),
  (99, 252, "Es", "Einsteinium"),
  (100, 257, "Fm", "Fermium"),
  (101, 258, "Md", "Mendelevium"),
  (102, 259, "No", "Nobelium"),
  (103, 262, "Lr", "Lawrencium"),
)

#let element(number, mass, symbol, name, fill: rgb("#cdd3da"), text-color: black) = {
  box(width: 3cm, height: 3cm, fill: fill, stroke: 0.7pt, inset: 4pt)[
    #set align(center)
    #text(size: 18pt, weight: "bold")[#number #h(1fr) #mass]\
    #v(1fr)
    #text(size: 40pt, weight: "bold", fill: text-color)[#symbol]\
    #v(1fr)
    #text(size: 13pt)[#name]
  ]
}

#canvas({
  let cell-size = 3.25 // Increased cell size
  let (start-x, start-y) = (0, 0)
  let lanthanide-gap = 2.5 // Gap before lanthanides/actinides

  // Function to calculate element position
  let pos(group, period) = {
    let y-offset = if period > 7 { lanthanide-gap } else { 0 }
    let x = start-x + (group - 1) * cell-size
    let y = start-y - (period - 1) * cell-size - y-offset
    (x, y)
  }

  // The f-block rows hang below the main table, starting under group 3
  let special-pos(num, is-actinide: false) = {
    let (row, first) = if is-actinide { (9, 89) } else { (8, 57) }
    pos(num - first + 3, row)
  }

  for period in range(1, elements.len() + 1) {
    for group in range(1, 19) {
      let data = elements.at(period - 1).at(group - 1)
      if data != none {
        content(pos(group, period), element(
          ..data.slice(0, 4),
          fill: data.at(4),
        ))
      }
    }
  }

  // Lanthanides
  for (idx, data) in lanthanides.enumerate() {
    content(special-pos(57 + idx), element(..data, fill: colors.lanthanide))
  }

  // Actinides
  for (idx, data) in actinides.enumerate() {
    // Ac through U occur naturally; everything past uranium is man-made, shown in gray
    let text-color = if idx <= 3 { black } else { colors.synthetic }
    content(
      special-pos(89 + idx, is-actinide: true),
      element(..data, fill: colors.lanthanide, text-color: text-color),
    )
  }

  // Period labels
  for period in range(1, 8) {
    content(
      (start-x - cell-size * 0.6, start-y - (period - 1) * cell-size),
      text(size: 16pt, weight: "bold")[#period],
    )
  }

  // Group labels
  let groups = "IA IIA IIIB IVB VB VIB VIIB VIIIB VIIIB VIIIB IB IIB IIIA IVA VA VIA VIIA VIIIA".split(
    " ",
  )

  // Find first element in each column
  for (num, label) in groups.enumerate(start: 1) {
    let first-period = if num == 1 { 1 } else if num == 2 { 2 } else if (
      num <= 12
    ) { 4 } else { 2 }
    let (x, y) = pos(num, first-period)
    content((x, y + cell-size * 0.7), box(width: 3cm)[
      #text(size: 14pt, weight: "bold")[#num #h(1fr) #label]
    ])
  }

  // Legend
  let legend-start = (start-x - 0.5 * cell-size, start-y - 6.8 * cell-size)
  let legend-items = (
    ("AlkaliMetal", colors.alkali-metal),
    ("AlkalineEarth etal", colors.alkaline-earth),
    ("TransitionMetal", colors.metal),
    ("Metalloid", colors.metalloid),
    ("Nonmetal", colors.nonmetal),
    ("Halogen", colors.halogen),
    ("NobleGas", colors.noble-gas),
    ("LanthanideActinide", colors.lanthanide),
  )

  for (idx, (label, color)) in legend-items.enumerate() {
    let y-offset = idx
    rect(
      (legend-start.at(0), legend-start.at(1) - y-offset),
      (legend-start.at(0) + 0.8, legend-start.at(1) - y-offset - 0.8),
      fill: color,
      stroke: 0.7pt,
    )
    content(
      (legend-start.at(0) + 1, legend-start.at(1) - y-offset - 0.4),
      text(size: 14pt)[#label],
      anchor: "west",
    )
  }

  // Element key
  let legend = element("Z", "mass", text("Symbol", size: 22pt), "Name")
  content((12, -4), legend)
  content((16.5, -4), text(size: 18pt)[
    black: natural\
    #text(fill: colors.synthetic)[gray: man-made]
  ])
})