@import url("https://cdn.jsdelivr.net/npm/comic-mono@0.0.1/index.css");

:root {
  font-family: "Comic Sans MS", serif;
}

code {
  font-family: "Comic Mono", monospace;
}

h1,
h2 {
  margin-block: 0;
}

h1 {
  font-size: 14px;
}

h2 {
  font-size: 12px;
}

marquee,ge-marquee{
   height: calc(100vh - 1lh);
}

.wrapper {
  width: 100%;
  columns: 4;
}

.wrapper ul {
  padding-inline-start: 0;
  list-style: none;
  font-size: 12px;
}

a, a:link,a:hover,a:visited,a:focus{
  color: blue;
}
View Compiled
//import data from "https://unpkg.com/@mdn/browser-compat-data" with { type: "json" };
import "https://unpkg.com/geo-elements@0.0.6/dist/geo-elements.iife.js";
const BCD = await (await fetch("https://unpkg.com/@mdn/browser-compat-data")).json();

const END_DATE = new Date();
const START_DATE = new Date(END_DATE.getFullYear() - 6, 0, 1);


const { browsers, css } = BCD;
const { chrome, safari, firefox } = browsers;

const $a = (node, attrs) => (
  Object.entries(Object(attrs)).forEach(([name, value]) =>
    node.setAttribute(name, value)
  ),
  node
);
const $c = (node, ...children) => (node.append(...children), node);
const $h = (name, attrs, ...children) =>
  $c($a(document.createElement(name), attrs), ...children);

const updateForGMT = (date) => (
  date.setTime(date.getTime() + date.getTimezoneOffset() * 60 * 1000), date
);

/** @type Date */
let startDate = updateForGMT(START_DATE);
let endDate = updateForGMT(END_DATE);



const h1 = $h("h1", {}, `CSS Features since ${startDate.getUTCFullYear()}-${(startDate.getUTCMonth() + 1+"").padStart(2, "0")}-${(startDate.getDate()+"").padStart(2,"0")}`)
$c(
  document.body,
  h1
);

let totalFeatures = 0;

for (const browser of [chrome, safari, firefox]) {
  for (const release of Object.values(browser.releases)) {

    let { release_date } = release;
    if(release_date){
      let [YYYY, MM, DD] = release_date.split("-");
      release_date = updateForGMT(new Date(+YYYY, MM - 1, +DD));
      Object.assign(release, { release_date });
    }
  }
}

let allSupportedFeatures = {};

for (const [category, features] of Object.entries(css)) {
  const featuresEntries = [...Object.entries(features)];
  for (const [featureName, feature] of featuresEntries) {
    if (!feature?.__compat) {
      console.log(featureName, feature);
      Object.entries(feature).forEach(([otherName, data]) => {
        featuresEntries.push([`${featureName}: ${otherName}`, data]);
      });
      continue;
    }
    const {
      __compat: {
        support: {
          chrome: { version_added: chromeVersion },
          firefox: { version_added: firefoxVersion },
          safari: { version_added: safariVersion }
        }
      },
      ...other
    } = feature;

    Object.entries(other).forEach(([otherName, data]) => {
      if (!data?.__compat?.mdn_url) {
        data.__compat.mdn_url = feature.__compat.mdn_url;
      }
      data.__compat.parentName = featureName;
      featuresEntries.push([`${featureName}: ${otherName}`, data]);
    });

    if (!chromeVersion) continue;
    if (!firefoxVersion) continue;
    if (!safariVersion) continue;

    if (!/^\d/.test(chromeVersion)) continue;
    if (!/^\d/.test(firefoxVersion)) continue;
    if (!/^\d/.test(safariVersion)) continue;

    const releaseDates = new Map();
    releaseDates.set(
      Number(chrome.releases[chromeVersion].release_date),
      "chrome " + chromeVersion
    );
    releaseDates.set(
      Number(firefox.releases[firefoxVersion].release_date),
      "firefox " + firefoxVersion
    );
    releaseDates.set(
      Number(safari.releases[safariVersion].release_date),
      "safari " + safariVersion
    );

    const firstAvailabilityTime = Math.min(...releaseDates.keys());

    const generalAvailabilityTime = Math.max(...releaseDates.keys());

    if (generalAvailabilityTime < startDate) continue;

    const firstAvailability = new Date(firstAvailabilityTime);
    const generalAvailability = new Date(generalAvailabilityTime);
    const firstAvailabilityBrowser = releaseDates.get(firstAvailabilityTime);
    const generalAvailabilityBrowser = releaseDates.get(
      generalAvailabilityTime
    );
    const year = generalAvailability.getUTCFullYear();

    allSupportedFeatures[year] = allSupportedFeatures[year] || {};
    allSupportedFeatures[year][category] =
      allSupportedFeatures[year][category] || [];

    allSupportedFeatures[year][category].push({
      category,
      feature: featureName,
      full: feature,
      firstAvailability,
      firstAvailabilityBrowser,
      generalAvailability,
      generalAvailabilityBrowser
    });
  }
}

console.log(allSupportedFeatures);

const $fragment = $h("div", { class: "wrapper" });

let idx = 0;

for (
  let YYYY = startDate.getUTCFullYear();
  YYYY <= endDate.getUTCFullYear();
  ++YYYY
) {
  if (!allSupportedFeatures[YYYY]) continue;
  let $heading = $h("h2", {}, `${YYYY} - `);

  $c($fragment, $heading);

  const currentYear = allSupportedFeatures[YYYY];

  let currentCategory = "";
  let $currentList = null;
  $currentList = $h("ul");
  let total = 0;

  for (const [currentCategory, features] of Object.entries(
    Object(currentYear)
  )) {
    total += features.length;
    totalFeatures += features.length;

    $c($fragment, $currentList);

    features.forEach((feature) => {
      const { description: descr, mdn_url, parentName } =
        feature?.full?.__compat || {};
      let description = $h("code", {}, feature.feature);
      if (feature?.full?.__compat?.description) {
        description = $h("code");
        description.innerHTML = (parentName ? `${parentName}: ` : "") + descr;
      }
      $c(
        $currentList,
        $h("li", {}, $h("a", { href: mdn_url, target: "_blank" }, description))
      );
    });
  }

  $c($heading, total);
}

h1.textContent += ` (total: ${totalFeatures})`;

const marquee = $h("ge-marquee", {direction:"up", scrollamount:"6", scrolldelay:"60"}, $fragment)

$c(document.body, marquee);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.