Wikipedia-Appearance_UI

by Jason Park

HTML

<div class="container">
  <div class="column-start">
    <div class="sticky-pinned-container">
      <div class="pinnable-header">Contents</div>
      <ul class="toc-contents">
        <li class="toc-level-1 toc-list-item">
          <a class="toc-link toc-link-active" href="#">
            <div class="toc-text">(Top)</div>
          </a>
        </li>
        <li class="toc-level-1 toc-list-item">
          <a class="toc-link" href="#Sentence_1">
            <div class="toc-text">Sentence 1</div>
          </a>
        </li>
        <li class="toc-level-1 toc-list-item toc-list-item-expanded">
          <a class="toc-link" href="#Sentence_2" class="toc-link">
            <div class="toc-text">Sentence 2</div>
          </a>
          <button
            aria-controls="toc-Background-sublist"
            class="toc-toggle-button"
            aria-expanded="true"
          >
            <span class="vector-icon icon-expand"></span>
          </button>
          <ul class="toc-contents">
            <li class="toc-level-2 toc-list-item">
              <a class="toc-link" href="#Sentence_3">
                <div class="toc-text">Sentence 3</div>
              </a>
            </li>
            <li class="toc-level-2 toc-list-item">
              <a class="toc-link" href="#Sentence_4">
                <div class="toc-text">Sentence 4</div>
              </a>
            </li>
          </ul>
        </li>
      </ul>
    </div>
  </div>

  <main class="main">
    <div class="content">
      <h1>Lorem Ipsum</h1>

      <h4>
        "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
        consectetur, adipisci velit..."
      </h4>

      <h5>
        "There is no one who loves pain itself, who seeks after it and wants to
        have it, simply because it is pain..."
      </h5>

      <hr />

      <p>
        Sollicitudin tristique blandit dictum viverra habitant luctus eleifend
        dis. Fringilla tristique tempor hac consectetur pellentesque...

CSS

:root {
  --background-color-base: #fff;
  --background-color-base-fixed: #fff;
  --background-color-interactive: #eaecf0;
  --background-color-neutral-subtle: #f8f9fa;
  --border-color-base: #a2a9b1;
  --border-color-interactive: #72777d;
  --border-color-progressive: #36c;
  --color-base: #202122;
  --color-subtle: #54595d;
  --color-progressive: #36c;
  --color-progressive--hover: #4b77d6;
  --color-visited: #6a60b0;

  body {
    background-color: var(--background-color-neutral-subtle, #f8f9fa);
    color: var(--color-base, #202122);
    font-family: sans-serif;
    margin: 0;
  }

  a {
    text-decoration: none;
    color: var(--color-progressive, #36c);
  }

  a:hover {
    color: var(--color-progressive--hover, #4b77d6);
    text-decoration: underline;
  }

  a:visited {
    color: var(--color-visited, #6a60b0);
  }

  a:visited:hover {
    color: var(--color-visited, #6a60b0);
  }

  p {
    margin: 0.5em 0 1em 0;
  }
}

.skin-theme-clientpref-night {
  --background-color-base: #101418;
  --background-color-interactive: #27292d;
  --background-color-neutral-subtle: #202122;
  --border-color-base: #72777d;
  --border-color-interactive: #a2a9b1;
  --color-base: #eaecf0;
  --color-subtle: #a2a9b1;
  --color-progressive: #88a3e8;
  --color-progressive--hover: #b0c1f0;
  --color-visited: #a799cd;
}

.feature-custom-font-size-clientpref-0 {
  --font-size-medium: 0.875rem;
  --line-height-medium: 1.5714285;
}

.feature-custom-font-size-clientpref-1 {
  --font-size-medium: 1rem;
  --line-height-medium: 1.6;
}

.feature-custom-font-size-clientpref-2 {
  --font-size-medium: 1.25rem;
  --line-height-medium: 1.5;
}

.feature-limited-width-clientpref-0 {
  max-width: none !important;
}

.container {
  background-color: var(--background-color-base, #fff);
  column-gap: 24px;
  display: grid;
  grid-template-columns: 10rem auto;
  grid-template-areas: "columnStart main";
  max-width: 50rem;
  margin: auto;
  padding: 16px;
}

.heading {
  display: flow-root;
  margin:...

JavaScript

// Set default appearance
document.documentElement.classList.add("feature-custom-font-size-clientpref-1")

if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
  document.documentElement.classList.add("skin-theme-clientpref-night")
}

document.querySelector(
  "#skin-client-pref-feature-custom-font-size-value-1",
).checked = true
document.querySelector(
  "#skin-client-pref-feature-limited-width-value-0",
).checked = true
document.querySelector("#skin-client-pref-skin-theme-value-os").checked = true

// Handle TOC click
document.querySelectorAll(".toc-link").forEach((item) => {
  item.addEventListener("click", (e) => {
    document.querySelectorAll(".toc-link-active").forEach((item) => {
      item.classList.remove("toc-link-active")
    })
    e.target.classList.add("toc-link-active")
  })
})

// Handle TOC arrow button
document.querySelector(".toc-toggle-button").addEventListener("click", (e) => {
  const currentTarget = e.currentTarget
  const parent = currentTarget.parentElement
  const isExpanded = currentTarget.getAttribute("aria-expanded") === "true"
  currentTarget.setAttribute("aria-expanded", isExpanded ? "false" : "true")
  if (isExpanded) {
    parent.classList.remove("toc-list-item-expanded")
  } else {
    parent.classList.add("toc-list-item-expanded")
  }
})

// Handle appearance text
for (let i = 0; i < 3; i++) {
  document
    .querySelector("#skin-client-pref-feature-custom-font-size-value-" + i)
    .addEventListener("change", (e) => {
      document.documentElement.classList.forEach((value, key, listObj) => {
        if (value.startsWith("feature-custom-font-size")) {
          document.documentElement.classList.remove(value)
        }
      })
      document.documentElement.classList.add(
        "feature-custom-font-size-clientpref-" + i,
      )
    })
}

// Handle page width
for (let i = 0; i < 2; i++) {
  document
    .querySelector("#skin-client-pref-feature-limited-width-value-" + i)
    .addEventListener("change", (e) => {
...