diff --git a/examples/docs/astro.config.mjs b/examples/docs/astro.config.mjs
index 68499b3fa..120106f3e 100644
--- a/examples/docs/astro.config.mjs
+++ b/examples/docs/astro.config.mjs
@@ -8,6 +8,10 @@
// @ts-check
export default /** @type {import('astro').AstroUserConfig} */ ({
- // Enable the Preact renderer to support Preact JSX components.
- renderers: ['@astrojs/renderer-preact'],
+ renderers: [
+ // Enable the Preact renderer to support Preact JSX components.
+ '@astrojs/renderer-preact',
+ // Enable the React renderer, for the Algolia search component
+ '@astrojs/renderer-react',
+ ],
});
diff --git a/examples/docs/package.json b/examples/docs/package.json
index cfaa832ac..9893e6496 100644
--- a/examples/docs/package.json
+++ b/examples/docs/package.json
@@ -8,10 +8,21 @@
"build": "astro build",
"preview": "astro preview"
},
+ "dependencies": {
+ "@docsearch/react": "^1.0.0-alpha.27"
+ },
"devDependencies": {
- "astro": "^0.20.0"
+ "astro": "^0.20.0",
+ "@snowpack/plugin-dotenv": "^2.1.0"
},
"snowpack": {
+ "alias": {
+ "components": "./src/components",
+ "~": "./src"
+ },
+ "plugins": [
+ "@snowpack/plugin-dotenv"
+ ],
"workspaceRoot": "../.."
}
}
diff --git a/examples/docs/public/code.css b/examples/docs/public/code.css
index ec735a676..3fbb26626 100644
--- a/examples/docs/public/code.css
+++ b/examples/docs/public/code.css
@@ -8,148 +8,89 @@
opacity: 0.7;
}
-.token.atrule {
- color: #c792ea;
+.token.plain-text,
+[class*='language-bash'] span.token,
+[class*='language-shell'] span.token {
+ color: hsla(var(--color-gray-90), 1);
}
-.token.attr-name {
- color: #ffcb6b;
+[class*='language-bash'] span.token,
+[class*='language-shell'] span.token {
+ font-style: bold;
}
-.token.attr-value {
- color: #a5e844;
-}
-
-.token.attribute {
- color: #a5e844;
-}
-
-.token.boolean {
- color: #c792ea;
-}
-
-.token.builtin {
- color: #ffcb6b;
-}
-
-.token.cdata {
- color: #80cbc4;
-}
-
-.token.char {
- color: #80cbc4;
-}
-
-.token.class {
- color: #ffcb6b;
-}
-
-.token.class-name {
- color: #f2ff00;
-}
-
-.token.comment {
- color: #616161;
-}
-
-.token.constant {
- color: #c792ea;
+.token.prolog,
+.token.comment,
+[class*='language-bash'] span.token.comment,
+[class*='language-shell'] span.token.comment {
+ color: hsla(var(--color-gray-70), 1);
}
+.token.selector,
+.token.tag,
+.token.unit,
+.token.url,
+.token.variable,
+.token.entity,
.token.deleted {
- color: #ff6666;
+ color: #fa5e5b;
}
-.token.doctype {
- color: #616161;
+.token.boolean,
+.token.constant,
+.token.doctype,
+.token.number,
+.token.regex,
+.token.builtin,
+.token.class,
+.token.hexcode,
+.token.class-name,
+.token.attr-name {
+ color: hsla(var(--color-yellow), 1);
}
-.token.entity {
- color: #ff6666;
-}
-
-.token.function {
- color: #c792ea;
-}
-
-.token.hexcode {
- color: #f2ff00;
-}
-
-.token.id {
- color: #c792ea;
- font-weight: bold;
+.token.atrule,
+.token.attribute,
+.token.attr-value .token.punctuation,
+.token.attr-value,
+.token.pseudo-class,
+.token.pseudo-element,
+.token.string {
+ color: hsla(var(--color-green), 1);
}
+.token.symbol,
+.token.function,
+.token.id,
.token.important {
- color: #c792ea;
+ color: hsla(var(--color-blue), 1);
+}
+
+.token.important,
+.token.id {
font-weight: bold;
}
+.token.cdata,
+.token.char,
+.token.property {
+ color: #23b1af;
+}
+
.token.inserted {
- color: #80cbc4;
+ color: hsla(var(--color-green), 1);
}
.token.keyword {
- color: #c792ea;
-}
-
-.token.number {
- color: #fd9170;
+ color: #ff657c;
+ font-style: italic;
}
.token.operator {
- color: #89ddff;
-}
-
-.token.prolog {
- color: #616161;
-}
-
-.token.property {
- color: #80cbc4;
-}
-
-.token.pseudo-class {
- color: #a5e844;
-}
-
-.token.pseudo-element {
- color: #a5e844;
+ color: hsla(var(--color-gray-70), 1);
}
+.token.attr-value .token.attr-equals,
.token.punctuation {
- color: #89ddff;
-}
-
-.token.regex {
- color: #f2ff00;
-}
-
-.token.selector {
- color: #ff6666;
-}
-
-.token.string {
- color: #a5e844;
-}
-
-.token.symbol {
- color: #c792ea;
-}
-
-.token.tag {
- color: #ff6666;
-}
-
-.token.unit {
- color: #fd9170;
-}
-
-.token.url {
- color: #ff6666;
-}
-
-.token.variable {
- color: #ff6666;
+ color: hsla(var(--color-gray-80), 1);
}
diff --git a/examples/docs/public/default-og-image.png b/examples/docs/public/default-og-image.png
new file mode 100644
index 000000000..97903207e
Binary files /dev/null and b/examples/docs/public/default-og-image.png differ
diff --git a/examples/docs/public/index.css b/examples/docs/public/index.css
index 358f5fdf4..aadc5c2f5 100644
--- a/examples/docs/public/index.css
+++ b/examples/docs/public/index.css
@@ -3,14 +3,19 @@
margin: 0;
}
+/* Global focus outline reset */
+*:focus:not(:focus-visible) {
+ outline: none;
+}
+
:root {
--user-font-scale: 1rem - 16px;
- --max-width: calc(100% - 2rem);
+ --max-width: calc(100% - 1rem);
}
@media (min-width: 50em) {
:root {
- --max-width: 48em;
+ --max-width: 46em;
}
}
@@ -20,8 +25,9 @@ body {
min-height: 100vh;
font-family: var(--font-body);
font-size: 1rem;
- font-size: clamp(0.875rem, 0.4626rem + 1.0309vw + var(--user-font-scale), 1.125rem);
- line-height: 1.625;
+ font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem);
+ line-height: 1.5;
+ max-width: 100vw;
}
nav ul {
@@ -29,18 +35,28 @@ nav ul {
padding: 0;
}
-.content main > * + * {
- margin-top: 1rem;
+.content > section > * + * {
+ margin-top: 1.25rem;
+}
+
+.content > section > :first-child {
+ margin-top: 0;
}
/* Typography */
-:is(h1, h2, h3, h4, h5, h6) {
- margin-bottom: 1.38rem;
- font-weight: 400;
- line-height: 1.3;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin-bottom: 1rem;
+ font-weight: bold;
+ line-height: 1;
}
-:is(h1, h2) {
+h1,
+h2 {
max-width: 40ch;
}
@@ -48,27 +64,41 @@ nav ul {
margin-top: 3rem;
}
+:is(h4, h5, h6):not(:first-child) {
+ margin-top: 2rem;
+}
+
h1 {
- font-size: clamp(2.488rem, 1.924rem + 1.41vw, 3.052rem);
+ font-size: 3.25rem;
+ font-weight: 800;
}
h2 {
- font-size: clamp(2.074rem, 1.707rem + 0.9175vw, 2.441rem);
+ font-size: 2.5rem;
}
h3 {
- font-size: clamp(1.728rem, 1.503rem + 0.5625vw, 1.953rem);
+ font-size: 1.75rem;
}
h4 {
- font-size: clamp(1.44rem, 1.317rem + 0.3075vw, 1.563rem);
+ font-size: 1.3rem;
}
h5 {
- font-size: clamp(1.2rem, 1.15rem + 0.125vw, 1.25rem);
+ font-size: 1rem;
}
p {
+ line-height: 1.65em;
+}
+
+.content ul {
+ line-height: 1.1em;
+}
+
+p,
+.content ul {
color: var(--theme-text-light);
}
@@ -78,18 +108,52 @@ small,
}
a {
- color: var(--theme-accent);
+ color: var(--theme-text-accent);
font-weight: 400;
text-underline-offset: 0.08em;
- text-decoration: none;
- display: inline-flex;
align-items: center;
gap: 0.5rem;
}
+article > section :is(ul, ol) > * + * {
+ margin-top: 0.75rem;
+}
+
+article > section nav :is(ul, ol) > * + * {
+ margin-top: inherit;
+}
+
+article > section li > :is(p, pre, blockquote):not(:first-child) {
+ margin-top: 1rem;
+}
+
+article > section :is(ul, ol) {
+ padding-left: 1em;
+}
+
+article > section nav :is(ul, ol) {
+ padding-left: inherit;
+}
+
+article > section nav {
+ margin-top: 1rem;
+ margin-bottom: 2rem;
+}
+
+article > section ::marker {
+ font-weight: bold;
+ color: var(--theme-text-light);
+}
+
+article > section iframe {
+ width: 100%;
+ height: auto;
+ aspect-ratio: 16 / 9;
+}
+
a > code:not([class*='language']) {
position: relative;
- color: var(--theme-accent);
+ color: var(--theme-text-accent);
background: transparent;
text-underline-offset: var(--padding-block);
}
@@ -123,19 +187,21 @@ strong {
}
/* Supporting Content */
+code {
+ font-family: var(--font-mono);
+ font-size: 0.85em;
+}
code:not([class*='language']) {
--border-radius: 3px;
--padding-block: 0.2rem;
- --padding-inline: 0.33rem;
-
- font-family: var(--font-mono);
- font-size: 0.85em;
- color: inherit;
+ --padding-inline: 0.4rem;
+ color: var(--theme-code-inline-text);
background-color: var(--theme-code-inline-bg);
padding: var(--padding-block) var(--padding-inline);
margin: calc(var(--padding-block) * -1) -0.125em;
border-radius: var(--border-radius);
+ box-shadow: 0 2px 1px 0 rgba(0, 0, 0, 0.08);
}
pre > code:not([class*='language']) {
@@ -146,37 +212,78 @@ pre > code:not([class*='language']) {
color: inherit;
}
+pre > code {
+ font-size: 1em;
+}
+
+table,
pre {
position: relative;
- background-color: var(--theme-code-bg);
- color: var(--theme-code-text);
--padding-block: 1rem;
--padding-inline: 2rem;
padding: var(--padding-block) var(--padding-inline);
padding-right: calc(var(--padding-inline) * 2);
- margin-left: calc(50vw - var(--padding-inline));
- transform: translateX(-50vw);
+ margin-left: calc(var(--padding-inline) * -1);
+ margin-right: calc(var(--padding-inline) * -1);
+ font-family: var(--font-mono);
- line-height: 1.414;
- width: calc(100vw + 4px);
- max-width: calc(100% + (var(--padding-inline) * 2));
+ line-height: 1.5;
+ font-size: 0.85em;
overflow-y: hidden;
overflow-x: auto;
}
+table {
+ width: 100%;
+ padding: var(--padding-block) 0;
+ margin: 0;
+ border-collapse: collapse;
+}
+
+/* Zebra striping */
+tr:nth-of-type(odd) {
+ background: var(--theme-bg-hover);
+}
+th {
+ background: var(--color-black);
+ color: var(--theme-color);
+ font-weight: bold;
+}
+td,
+th {
+ padding: 6px;
+ text-align: left;
+}
+
+pre {
+ background-color: var(--theme-code-bg);
+ color: var(--theme-code-text);
+}
+
+blockquote code:not([class*='language']) {
+ background-color: var(--theme-bg);
+}
+
@media (min-width: 37.75em) {
pre {
--padding-inline: 1.25rem;
border-radius: 8px;
+ margin-left: 0;
+ margin-right: 0;
}
}
blockquote {
margin: 2rem 0;
- padding: 0.5em 1rem;
- border-left: 3px solid rgba(0, 0, 0, 0.35);
- background-color: rgba(0, 0, 0, 0.05);
+ padding: 1.25em 1.5rem;
+ border-left: 3px solid var(--theme-text-light);
+ background-color: var(--theme-bg-offset);
border-radius: 0 0.25rem 0.25rem 0;
+ line-height: 1.7;
+}
+
+img {
+ max-width: 100%;
}
.flex {
@@ -197,92 +304,43 @@ button {
align-items: center;
gap: 0.25em;
border-radius: 99em;
- background-color: var(--theme-bg);
-}
-button:hover {
-}
-
-#theme-toggle {
- display: flex;
- align-items: center;
- gap: 0.25em;
- padding: 0.33em 0.67em;
- margin-left: -0.67em;
- margin-right: -0.67em;
- border-radius: 99em;
+ color: var(--theme-text);
background-color: var(--theme-bg);
}
-#theme-toggle > label:focus-within {
- outline: 2px solid transparent;
- box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+h2.heading {
+ font-size: 1rem;
+ font-weight: 700;
+ padding: 0.1rem 1rem;
+ text-transform: uppercase;
+ margin-bottom: 0.5rem;
}
-#theme-toggle > label {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0.5;
- transition: transform 120ms ease-out, opacity 120ms ease-out;
-}
-
-#theme-toggle > label:hover,
-#theme-toggle > label:focus {
- transform: scale(1.125);
- opacity: 1;
-}
-
-#theme-toggle .checked {
- color: var(--theme-accent);
- transform: scale(1.125);
- opacity: 1;
-}
-
-input[name='theme-toggle'] {
- position: absolute;
- opacity: 0;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: -1;
-}
-
-nav h4 {
- font-weight: 400;
- font-size: 1.25rem;
- margin: 0;
- margin-bottom: 1em;
-}
-
-.edit-on-github,
.header-link {
font-size: 1rem;
- padding-left: 1rem;
+ padding: 0.1rem 0 0.1rem 1rem;
border-left: 4px solid var(--theme-divider);
}
-.edit-on-github:hover,
-.edit-on-github:focus,
.header-link:hover,
.header-link:focus {
- color: var(--theme-text-light);
- border-left-color: var(--theme-text-lighter);
-}
-
-.header-link:focus-within {
- color: var(--theme-text-light);
- border-left-color: var(--theme-text-lighter);
-}
-
-.header-link.active {
border-left-color: var(--theme-accent);
color: var(--theme-accent);
}
-
-.header-link.depth-2 {
- font-weight: 600;
+.header-link:focus-within {
+ color: var(--theme-text-light);
+ border-left-color: hsla(var(--color-gray-40), 1);
+}
+.header-link svg {
+ opacity: 0.6;
+}
+.header-link:hover svg {
+ opacity: 0.8;
+}
+.header-link a {
+ display: inline-flex;
+ gap: 0.5em;
+ width: 100%;
}
.header-link.depth-3 {
@@ -292,88 +350,37 @@ nav h4 {
padding-left: 3rem;
}
-.edit-on-github,
.header-link a {
font: inherit;
color: inherit;
text-decoration: none;
}
-.edit-on-github {
- margin-top: 2rem;
- text-decoration: none;
-}
-.edit-on-github > * {
- text-decoration: none;
+/* Screenreader Only Text */
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
}
-.nav-link {
- font-size: 1rem;
- margin-bottom: 0;
- transform: translateX(0);
- transition: 120ms transform ease-out;
+.focus\:not-sr-only:focus,
+.focus\:not-sr-only:focus-visible {
+ position: static;
+ width: auto;
+ height: auto;
+ padding: 0;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+ white-space: normal;
}
-.nav-link:hover,
-.nav-link:focus {
- color: var(--theme-text-lighter);
- transform: translateX(0.25em);
+:target {
+ scroll-margin: calc(var(--theme-sidebar-offset, 5rem) + 2rem) 0 2rem;
}
-
-.nav-link:focus-within {
- color: var(--theme-text-lighter);
- transform: translateX(0.25em);
-}
-
-.nav-link a {
- font: inherit;
- color: inherit;
- text-decoration: none;
-}
-
-.nav-groups {
- padding-bottom: 2rem;
- max-height: calc(100% - 3rem);
- overflow-y: auto;
- overflow-x: hidden;
-}
-
-.nav-groups > li + li {
- margin-top: 2rem;
-}
-
-/* Scrollbar */
-
-/* Firefox */
-body {
- scrollbar-width: thin;
- scrollbar-color: var(--theme-text-lighter) var(--theme-divider);
-}
-
-/* width */
-::-webkit-scrollbar {
- width: 0.5rem;
-}
-
-/* Track */
-::-webkit-scrollbar-track {
- background: var(--theme-divider);
- border-radius: 1rem;
-}
-
-/* Handle */
-::-webkit-scrollbar-thumb {
- background: var(--theme-text-lighter);
- border-radius: 1rem;
-}
-
-/* Handle on hover */
-::-webkit-scrollbar-thumb:hover {
- background: var(--theme-text-light);
-}
-
-/* Buttons */
-::-webkit-scrollbar-button {
- display: none;
-}
-/* Scrollbar - End */
diff --git a/examples/docs/public/make-scrollable-code-focusable.js b/examples/docs/public/make-scrollable-code-focusable.js
new file mode 100644
index 000000000..35f104923
--- /dev/null
+++ b/examples/docs/public/make-scrollable-code-focusable.js
@@ -0,0 +1,3 @@
+Array.from(document.getElementsByTagName('pre')).forEach((element) => {
+ element.setAttribute('tabindex', '0');
+});
diff --git a/examples/docs/public/theme.css b/examples/docs/public/theme.css
index 7a4613188..587ddf29d 100644
--- a/examples/docs/public/theme.css
+++ b/examples/docs/public/theme.css
@@ -1,51 +1,81 @@
:root {
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
--font-body: system-ui, var(--font-fallback);
- --font-mono: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
+ --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono',
+ 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;
- --color-white: #fff;
- --color-black: #000014;
+ /*
+ * Variables with --color-base prefix define
+ * the hue, and saturation values to be used for
+ * hsla colors.
+ *
+ * ex:
+ *
+ * --color-base-{color}: {hue}, {saturation};
+ *
+ */
- --color-gray-50: #f9fafb;
- --color-gray-100: #f3f4f6;
- --color-gray-200: #e5e7eb;
- --color-gray-300: #d1d5db;
- --color-gray-400: #9ca3af;
- --color-gray-500: #6b7280;
- --color-gray-600: #4b5563;
- --color-gray-700: #374151;
- --color-gray-800: #1f2937;
- --color-gray-900: #111827;
+ --color-base-white: 0, 0%;
+ --color-base-black: 240, 100%;
+ --color-base-gray: 215, 14%;
+ --color-base-blue: 212, 100%;
+ --color-base-blue-dark: 212, 72%;
+ --color-base-green: 158, 79%;
+ --color-base-orange: 22, 100%;
+ --color-base-purple: 269, 79%;
+ --color-base-red: 351, 100%;
+ --color-base-yellow: 41, 100%;
- --color-blue: #3894ff;
- --color-blue-rgb: 56, 148, 255;
- --color-green: #17c083;
- --color-green-rgb: 23, 192, 131;
- --color-orange: #ff5d01;
- --color-orange-rgb: 255, 93, 1;
- --color-purple: #882de7;
- --color-purple-rgb: 136, 45, 231;
- --color-red: #ff1639;
- --color-red-rgb: 255, 22, 57;
- --color-yellow: #ffbe2d;
- --color-yellow-rgb: 255, 190, 45;
+ /*
+ * Color palettes are made using --color-base
+ * variables, along with a lightness value to
+ * define different variants.
+ *
+ */
+
+ --color-gray-5: var(--color-base-gray), 5%;
+ --color-gray-10: var(--color-base-gray), 10%;
+ --color-gray-20: var(--color-base-gray), 20%;
+ --color-gray-30: var(--color-base-gray), 30%;
+ --color-gray-40: var(--color-base-gray), 40%;
+ --color-gray-50: var(--color-base-gray), 50%;
+ --color-gray-60: var(--color-base-gray), 60%;
+ --color-gray-70: var(--color-base-gray), 70%;
+ --color-gray-80: var(--color-base-gray), 80%;
+ --color-gray-90: var(--color-base-gray), 90%;
+ --color-gray-95: var(--color-base-gray), 95%;
+
+ --color-blue: var(--color-base-blue), 61%;
+ --color-blue-dark: var(--color-base-blue-dark), 39%;
+ --color-green: var(--color-base-green), 42%;
+ --color-orange: var(--color-base-orange), 50%;
+ --color-purple: var(--color-base-purple), 54%;
+ --color-red: var(--color-base-red), 54%;
+ --color-yellow: var(--color-base-yellow), 59%;
}
:root {
color-scheme: light;
- --theme-accent: var(--color-blue);
- --theme-accent-rgb: var(--color-blue-rgb);
+ --theme-accent: hsla(var(--color-orange), 1);
+ --theme-text-accent: hsla(var(--color-orange), 1);
--theme-accent-opacity: 0.1;
- --theme-divider: var(--color-gray-100);
- --theme-text: var(--color-gray-800);
- --theme-text-light: var(--color-gray-600);
- --theme-text-lighter: var(--color-gray-400);
- --theme-bg: var(--color-white);
- --theme-bg-offset: var(--color-gray-100);
- --theme-bg-accent: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
- --theme-code-inline-bg: var(--color-gray-100);
- --theme-code-text: var(--color-gray-100);
- --theme-code-bg: var(--color-gray-700);
+ --theme-divider: hsla(var(--color-gray-95), 1);
+ --theme-text: hsla(var(--color-gray-10), 1);
+ --theme-text-light: hsla(var(--color-gray-40), 1);
+ /* @@@: not used anywhere */
+ --theme-text-lighter: hsla(var(--color-gray-80), 1);
+ --theme-bg: hsla(var(--color-base-white), 100%, 1);
+ --theme-bg-hover: hsla(var(--color-gray-95), 1);
+ --theme-bg-offset: hsla(var(--color-gray-90), 1);
+ --theme-bg-accent: hsla(var(--color-orange), var(--theme-accent-opacity));
+ --theme-code-inline-bg: hsla(var(--color-gray-95), 1);
+ --theme-code-inline-text: var(--theme-text);
+ --theme-code-bg: hsla(217, 19%, 27%, 1);
+ --theme-code-text: hsla(var(--color-gray-95), 1);
+ --theme-navbar-bg: hsla(var(--color-base-white), 100%, 1);
+ --theme-navbar-height: 6rem;
+ --theme-selection-color: hsla(var(--color-orange), 1);
+ --theme-selection-bg: hsla(var(--color-orange), var(--theme-accent-opacity));
}
body {
@@ -55,19 +85,39 @@ body {
:root.theme-dark {
color-scheme: dark;
- --theme-accent-opacity: 0.3;
- --theme-divider: var(--color-gray-900);
- --theme-text: var(--color-gray-200);
- --theme-text-light: var(--color-gray-400);
- --theme-text-lighter: var(--color-gray-600);
- --theme-bg: var(--color-black);
- --theme-bg-offset: var(--color-gray-900);
- --theme-code-inline-bg: var(--color-gray-800);
- --theme-code-text: var(--color-gray-200);
- --theme-code-bg: var(--color-gray-900);
+ --theme-accent-opacity: 0.4;
+ --theme-accent: hsla(var(--color-orange), 1);
+ --theme-text-accent: hsla(var(--color-orange), 1);
+ --theme-divider: hsla(var(--color-gray-10), 1);
+ --theme-text: hsla(var(--color-gray-90), 1);
+ --theme-text-light: hsla(var(--color-gray-80), 1);
+
+ /* @@@: not used anywhere */
+ --theme-text-lighter: hsla(var(--color-gray-40), 1);
+ --theme-bg: hsla(215, 28%, 17%, 1);
+ --theme-bg-hover: hsla(var(--color-gray-40), 1);
+ --theme-bg-offset: hsla(var(--color-gray-5), 1);
+ --theme-code-inline-bg: hsla(var(--color-gray-10), 1);
+ --theme-code-inline-text: hsla(var(--color-base-white), 100%, 1);
+ --theme-code-bg: hsla(var(--color-gray-5), 1);
+ --theme-code-text: hsla(var(--color-base-white), 100%, 1);
+ --theme-navbar-bg: hsla(215, 28%, 17%, 1);
+ --theme-selection-color: hsla(var(--color-base-white), 100%, 1);
+ --theme-selection-bg: hsla(var(--color-purple), var(--theme-accent-opacity));
+
+ /* DocSearch [Algolia] */
+ --docsearch-modal-background: var(--theme-bg);
+ --docsearch-searchbox-focus-background: var(--theme-divider);
+ --docsearch-footer-background: var(--theme-divider);
+ --docsearch-text-color: var(--theme-text);
+ --docsearch-hit-background: var(--theme-divider);
+ --docsearch-hit-shadow: none;
+ --docsearch-hit-color: var(--theme-text);
+ --docsearch-footer-shadow: inset 0 2px 10px #000;
+ --docsearch-modal-shadow: inset 0 0 8px #000;
}
::selection {
- color: var(--theme-accent);
- background-color: rgba(var(--theme-accent-rgb), var(--theme-accent-opacity));
+ color: var(--theme-selection-color);
+ background-color: var(--theme-selection-bg);
}
diff --git a/examples/docs/public/theme.js b/examples/docs/public/theme.js
deleted file mode 100644
index d75d0bf99..000000000
--- a/examples/docs/public/theme.js
+++ /dev/null
@@ -1,8 +0,0 @@
-(() => {
- const root = document.documentElement;
- if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
- root.classList.add('theme-dark');
- } else {
- root.classList.remove('theme-dark');
- }
-})();
diff --git a/examples/docs/src/components/AvatarList.astro b/examples/docs/src/components/AvatarList.astro
deleted file mode 100644
index aafcb371b..000000000
--- a/examples/docs/src/components/AvatarList.astro
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
diff --git a/examples/docs/src/components/DocSidebar.tsx b/examples/docs/src/components/DocSidebar.tsx
deleted file mode 100644
index 076d460cc..000000000
--- a/examples/docs/src/components/DocSidebar.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import type { FunctionalComponent } from 'preact';
-import { h } from 'preact';
-import { useState, useEffect, useRef } from 'preact/hooks';
-import EditOnGithub from './EditOnGithub';
-
-const DocSidebar: FunctionalComponent<{ headers: any[]; editHref: string }> = ({ headers = [], editHref }) => {
- const itemOffsets = useRef([]);
- const [activeId, setActiveId] = useState(undefined);
-
- useEffect(() => {
- const getItemOffsets = () => {
- const titles = document.querySelectorAll('article :is(h2, h3, h4)');
- itemOffsets.current = Array.from(titles).map((title) => ({
- id: title.id,
- topOffset: title.getBoundingClientRect().top + window.scrollY,
- }));
- };
-
- const onScroll = () => {
- const itemIndex = itemOffsets.current.findIndex((item) => item.topOffset > window.scrollY + window.innerHeight / 3);
- if (itemIndex === 0) {
- setActiveId(undefined);
- } else if (itemIndex === -1) {
- setActiveId(itemOffsets.current[itemOffsets.current.length - 1].id);
- } else {
- setActiveId(itemOffsets.current[itemIndex - 1].id);
- }
- };
-
- getItemOffsets();
- window.addEventListener('resize', getItemOffsets);
- window.addEventListener('scroll', onScroll);
-
- return () => {
- window.removeEventListener('resize', getItemOffsets);
- window.removeEventListener('scroll', onScroll);
- };
- }, []);
-
- return (
-
- );
-};
-
-export default DocSidebar;
diff --git a/examples/docs/src/components/EditOnGithub.tsx b/examples/docs/src/components/EditOnGithub.tsx
deleted file mode 100644
index f7478934f..000000000
--- a/examples/docs/src/components/EditOnGithub.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import type { FunctionalComponent } from 'preact';
-import { h } from 'preact';
-
-const EditOnGithub: FunctionalComponent<{ href: string }> = ({ href }) => {
- return (
-
-
- Edit on GitHub
-
- );
-};
-
-export default EditOnGithub;
diff --git a/examples/docs/src/components/Footer/AvatarList.astro b/examples/docs/src/components/Footer/AvatarList.astro
new file mode 100644
index 000000000..589e296b9
--- /dev/null
+++ b/examples/docs/src/components/Footer/AvatarList.astro
@@ -0,0 +1,151 @@
+---
+// fetch all commits for just this page's path
+const path = "docs/" + Astro.props.path;
+const url = `https://api.github.com/repos/snowpackjs/astro/commits?path=${path}`;
+const commitsURL = `https://github.com/snowpackjs/astro/commits/main/${path}`;
+
+async function getCommits(url) {
+ try {
+ const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN;
+ if (!token) {
+ throw new Error(
+ 'Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'
+ );
+ }
+
+ const auth = `Basic ${Buffer.from(token, "binary").toString("base64")}`;
+
+ const res = await fetch(url, {
+ method: "GET",
+ headers: {
+ Authorization: auth,
+ "User-Agent": "astro-docs/1.0",
+ },
+ });
+
+ const data = await res.json();
+
+ if (!res.ok) {
+ throw new Error(
+ `Request to fetch commits failed. Reason: ${res.statusText}
+ Message: ${data.message}`
+ );
+ }
+
+ return data;
+ } catch (e) {
+ console.warn(`[error] /src/components/AvatarList.astro
+ ${e?.message ?? e}`);
+ return new Array();
+ }
+}
+
+function removeDups(arr) {
+ if (!arr) {
+ return new Array();
+ }
+ let map = new Map();
+
+ for (let item of arr) {
+ let author = item.author;
+ // Deduplicate based on author.id
+ map.set(author.id, { login: author.login, id: author.id });
+ }
+
+ return Array.from(map.values());
+}
+
+const data = await getCommits(url);
+const unique = removeDups(data);
+const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors
+const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors
+
+---
+
+
+
+
diff --git a/examples/docs/src/components/ArticleFooter.astro b/examples/docs/src/components/Footer/Footer.astro
similarity index 76%
rename from examples/docs/src/components/ArticleFooter.astro
rename to examples/docs/src/components/Footer/Footer.astro
index 8078e2cc3..48de51054 100644
--- a/examples/docs/src/components/ArticleFooter.astro
+++ b/examples/docs/src/components/Footer/Footer.astro
@@ -1,9 +1,10 @@
---
import AvatarList from './AvatarList.astro';
+const { path } = Astro.props;
---
+ Logo
+
+
+
\ No newline at end of file
diff --git a/examples/docs/src/components/Header/Header.astro b/examples/docs/src/components/Header/Header.astro
new file mode 100644
index 000000000..cc54585b5
--- /dev/null
+++ b/examples/docs/src/components/Header/Header.astro
@@ -0,0 +1,158 @@
+---
+import SkipToContent from './SkipToContent.astro';
+import SidebarToggle from './SidebarToggle.tsx';
+import LanguageSelect from './LanguageSelect.jsx';
+import Search from "./Search.jsx";
+import { getLanguageFromURL } from '../util.ts';
+
+const {currentPage} = Astro.props;
+const lang = currentPage && getLanguageFromURL(currentPage);
+---
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/docs/src/components/Header/LanguageSelect.css b/examples/docs/src/components/Header/LanguageSelect.css
new file mode 100644
index 000000000..4e878714b
--- /dev/null
+++ b/examples/docs/src/components/Header/LanguageSelect.css
@@ -0,0 +1,47 @@
+.language-select {
+ flex-grow: 1;
+ width: 48px;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0.33em 0.5em;
+ overflow: visible;
+ font-weight: 500;
+ font-size: 1rem;
+ font-family: inherit;
+ line-height: inherit;
+ background-color: var(--theme-bg);
+ border-color: var(--theme-text-lighter);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ outline: 0;
+ cursor: pointer;
+ transition-timing-function: ease-out;
+ transition-duration: 0.2s;
+ transition-property: border-color, color;
+ -webkit-font-smoothing: antialiased;
+ padding-left: 30px;
+ padding-right: 1rem;
+}
+.language-select-wrapper .language-select:hover,
+.language-select-wrapper .language-select:focus {
+ color: var(--theme-text);
+ border-color: var(--theme-text-light);
+}
+.language-select-wrapper {
+ color: var(--theme-text-light);
+ position: relative;
+}
+.language-select-wrapper > svg {
+ position: absolute;
+ top: 7px;
+ left: 10px;
+ pointer-events: none;
+}
+
+@media (min-width: 50em) {
+ .language-select {
+ width: 100%;
+ }
+}
diff --git a/examples/docs/src/components/Header/LanguageSelect.tsx b/examples/docs/src/components/Header/LanguageSelect.tsx
new file mode 100644
index 000000000..cf325eedc
--- /dev/null
+++ b/examples/docs/src/components/Header/LanguageSelect.tsx
@@ -0,0 +1,38 @@
+import type { FunctionalComponent } from 'preact';
+import { h } from 'preact';
+import './LanguageSelect.css';
+import { LANGUAGE_NAMES, langPathRegex } from '../../languages';
+
+const LanguageSelect: FunctionalComponent<{ lang: string }> = ({ lang }) => {
+ return (
+
+
+
+
+ );
+};
+
+export default LanguageSelect;
diff --git a/examples/docs/src/components/Header/Search.css b/examples/docs/src/components/Header/Search.css
new file mode 100644
index 000000000..2056c2c8f
--- /dev/null
+++ b/examples/docs/src/components/Header/Search.css
@@ -0,0 +1,76 @@
+/** Style Algolia */
+:root {
+ --docsearch-primary-color: var(--theme-accent);
+ --docsearch-logo-color: var(--theme-text);
+}
+.search-input {
+ flex-grow: 1;
+ box-sizing: border-box;
+ width: 100%;
+ margin: 0;
+ padding: 0.33em 0.5em;
+ overflow: visible;
+ font-weight: 500;
+ font-size: 1rem;
+ font-family: inherit;
+ line-height: inherit;
+ background-color: var(--theme-divider);
+ border-color: var(--theme-divider);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ outline: 0;
+ cursor: pointer;
+ transition-timing-function: ease-out;
+ transition-duration: 0.2s;
+ transition-property: border-color, color;
+ -webkit-font-smoothing: antialiased;
+}
+.search-input:hover,
+.search-input:focus {
+ color: var(--theme-text);
+ border-color: var(--theme-text-light);
+}
+.search-input:hover::placeholder,
+.search-input:focus::placeholder {
+ color: var(--theme-text-light);
+}
+.search-input::placeholder {
+ color: var(--theme-text-light);
+}
+.search-hint {
+ position: absolute;
+ top: 7px;
+ right: 19px;
+ padding: 3px 5px;
+ display: none;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ letter-spacing: 0.125em;
+ font-size: 13px;
+ font-family: var(--font-mono);
+ pointer-events: none;
+ border-color: var(--theme-text-lighter);
+ color: var(--theme-text-light);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.25rem;
+ line-height: 14px;
+}
+
+@media (min-width: 50em) {
+ .search-hint {
+ display: flex;
+ }
+}
+
+/* ------------------------------------------------------------ *\
+ DocSearch (Algolia)
+\* ------------------------------------------------------------ */
+
+.DocSearch-Modal .DocSearch-Hit a {
+ box-shadow: none;
+ border: 1px solid var(--theme-accent);
+}
diff --git a/examples/docs/src/components/Header/Search.tsx b/examples/docs/src/components/Header/Search.tsx
new file mode 100644
index 000000000..d842e007f
--- /dev/null
+++ b/examples/docs/src/components/Header/Search.tsx
@@ -0,0 +1,76 @@
+/* jsxImportSource: react */
+import { useState, useCallback, useRef } from 'react';
+import { createPortal } from 'react-dom';
+import { DocSearchModal, useDocSearchKeyboardEvents } from '@docsearch/react';
+import '@docsearch/css//dist/style.css';
+import './Search.css';
+
+export default function Search() {
+ const [isOpen, setIsOpen] = useState(false);
+ const searchButtonRef = useRef();
+ const [initialQuery, setInitialQuery] = useState(null);
+
+ const onOpen = useCallback(() => {
+ setIsOpen(true);
+ }, [setIsOpen]);
+
+ const onClose = useCallback(() => {
+ setIsOpen(false);
+ }, [setIsOpen]);
+
+ const onInput = useCallback(
+ (e) => {
+ setIsOpen(true);
+ setInitialQuery(e.key);
+ },
+ [setIsOpen, setInitialQuery]
+ );
+
+ useDocSearchKeyboardEvents({
+ isOpen,
+ onOpen,
+ onClose,
+ onInput,
+ searchButtonRef,
+ });
+
+ return (
+ <>
+
+ {isOpen &&
+ createPortal(
+ {
+ return items.map((item) => {
+ // We transform the absolute URL into a relative URL to
+ // work better on localhost, preview URLS.
+ const a = document.createElement('a');
+ a.href = item.url;
+ const hash = a.hash === '#overview' ? '' : a.hash;
+ return {
+ ...item,
+ url: `${a.pathname}${hash}`,
+ };
+ });
+ }}
+ />,
+ document.body
+ )}
+ >
+ );
+}
diff --git a/examples/docs/src/components/Header/SidebarToggle.tsx b/examples/docs/src/components/Header/SidebarToggle.tsx
new file mode 100644
index 000000000..97fece6b2
--- /dev/null
+++ b/examples/docs/src/components/Header/SidebarToggle.tsx
@@ -0,0 +1,27 @@
+import type { FunctionalComponent } from 'preact';
+import { h, Fragment } from 'preact';
+import { useState, useEffect } from 'preact/hooks';
+
+const MenuToggle: FunctionalComponent = () => {
+ const [sidebarShown, setSidebarShown] = useState(false);
+
+ useEffect(() => {
+ const body = document.getElementsByTagName('body')[0];
+ if (sidebarShown) {
+ body.classList.add('mobile-sidebar-toggle');
+ } else {
+ body.classList.remove('mobile-sidebar-toggle');
+ }
+ }, [sidebarShown]);
+
+ return (
+
+ );
+};
+
+export default MenuToggle;
diff --git a/examples/docs/src/components/Header/SkipToContent.astro b/examples/docs/src/components/Header/SkipToContent.astro
new file mode 100644
index 000000000..6df3a72ed
--- /dev/null
+++ b/examples/docs/src/components/Header/SkipToContent.astro
@@ -0,0 +1,21 @@
+
+Skip to Content
diff --git a/examples/docs/src/components/LeftSidebar/LeftSidebar.astro b/examples/docs/src/components/LeftSidebar/LeftSidebar.astro
new file mode 100644
index 000000000..96bd36fba
--- /dev/null
+++ b/examples/docs/src/components/LeftSidebar/LeftSidebar.astro
@@ -0,0 +1,109 @@
+---
+import { SIDEBAR } from '../../config.ts';
+import { getLanguageFromURL } from '../util.ts';
+const {currentPage} = Astro.props;
+const currentPageMatch = currentPage.slice(1);
+const langCode = getLanguageFromURL(currentPage);
+// SIDEBAR is a flat array. Group it by sections to properly render.
+const sidebarSections = SIDEBAR[langCode].reduce((col, item) => {
+ if (item.header) {
+ col.push({...item, children: []});
+ } else {
+ col[col.length-1].children.push(item);
+ }
+ return col;
+}, []);
+
+---
+
+
+
+
+
+
diff --git a/examples/docs/src/components/Note.astro b/examples/docs/src/components/Note.astro
deleted file mode 100644
index c57ede3a0..000000000
--- a/examples/docs/src/components/Note.astro
+++ /dev/null
@@ -1,52 +0,0 @@
----
-export interface Props {
- title: string;
- type?: 'tip' | 'warning' | 'error'
-}
-const { type = 'tip', title } = Astro.props;
----
-
-
-
-
diff --git a/examples/docs/src/components/PageContent/PageContent.astro b/examples/docs/src/components/PageContent/PageContent.astro
new file mode 100644
index 000000000..fd1e9d242
--- /dev/null
+++ b/examples/docs/src/components/PageContent/PageContent.astro
@@ -0,0 +1,41 @@
+---
+const {content, githubEditUrl} = Astro.props;
+const title = content.title;
+const headers = content.astro.headers;
+import MoreMenu from '../RightSidebar/MoreMenu.astro';
+import TableOfContents from '../RightSidebar/TableOfContents.tsx';
+---
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/docs/src/components/RightSidebar/MoreMenu.astro b/examples/docs/src/components/RightSidebar/MoreMenu.astro
new file mode 100644
index 000000000..6be2d86ee
--- /dev/null
+++ b/examples/docs/src/components/RightSidebar/MoreMenu.astro
@@ -0,0 +1,68 @@
+---
+import ThemeToggleButton from './ThemeToggleButton.jsx';
+const {editHref} = Astro.props;
+---
+
+More
+
+
+
+
diff --git a/examples/docs/src/components/RightSidebar/RightSidebar.astro b/examples/docs/src/components/RightSidebar/RightSidebar.astro
new file mode 100644
index 000000000..ed1dd37cc
--- /dev/null
+++ b/examples/docs/src/components/RightSidebar/RightSidebar.astro
@@ -0,0 +1,25 @@
+---
+import TableOfContents from './TableOfContents.jsx';
+import MoreMenu from './MoreMenu.astro';
+const {content, githubEditUrl} = Astro.props;
+const headers = content.astro.headers;
+---
+
+
\ No newline at end of file
diff --git a/examples/docs/src/components/RightSidebar/TableOfContents.tsx b/examples/docs/src/components/RightSidebar/TableOfContents.tsx
new file mode 100644
index 000000000..d8ea998d4
--- /dev/null
+++ b/examples/docs/src/components/RightSidebar/TableOfContents.tsx
@@ -0,0 +1,45 @@
+import type { FunctionalComponent } from 'preact';
+import { h, Fragment } from 'preact';
+import { useState, useEffect, useRef } from 'preact/hooks';
+
+const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({ headers = [] }) => {
+ const itemOffsets = useRef([]);
+ const [activeId, setActiveId] = useState(undefined);
+
+ useEffect(() => {
+ const getItemOffsets = () => {
+ const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
+ itemOffsets.current = Array.from(titles).map((title) => ({
+ id: title.id,
+ topOffset: title.getBoundingClientRect().top + window.scrollY,
+ }));
+ };
+
+ getItemOffsets();
+ window.addEventListener('resize', getItemOffsets);
+
+ return () => {
+ window.removeEventListener('resize', getItemOffsets);
+ };
+ }, []);
+
+ return (
+ <>
+ On this page
+
+
+ {headers
+ .filter(({ depth }) => depth > 1 && depth < 4)
+ .map((header) => (
+
+ ))}
+
+ >
+ );
+};
+
+export default TableOfContents;
diff --git a/examples/docs/src/components/RightSidebar/ThemeToggleButton.css b/examples/docs/src/components/RightSidebar/ThemeToggleButton.css
new file mode 100644
index 000000000..7de231d1b
--- /dev/null
+++ b/examples/docs/src/components/RightSidebar/ThemeToggleButton.css
@@ -0,0 +1,37 @@
+.theme-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25em;
+ padding: 0.33em 0.67em;
+ border-radius: 99em;
+ background-color: var(--theme-code-inline-bg);
+}
+
+.theme-toggle > label:focus-within {
+ outline: 2px solid transparent;
+ box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
+}
+
+.theme-toggle > label {
+ color: var(--theme-code-inline-text);
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0.5;
+}
+
+.theme-toggle .checked {
+ color: var(--theme-accent);
+ opacity: 1;
+}
+
+input[name='theme-toggle'] {
+ position: absolute;
+ opacity: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -1;
+}
diff --git a/examples/docs/src/components/ThemeToggle.tsx b/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx
similarity index 53%
rename from examples/docs/src/components/ThemeToggle.tsx
rename to examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx
index 5a5061c15..75ea775f4 100644
--- a/examples/docs/src/components/ThemeToggle.tsx
+++ b/examples/docs/src/components/RightSidebar/ThemeToggleButton.tsx
@@ -1,17 +1,11 @@
import type { FunctionalComponent } from 'preact';
import { h, Fragment } from 'preact';
import { useState, useEffect } from 'preact/hooks';
+import './ThemeToggleButton.css';
-const themes = ['system', 'light', 'dark'];
+const themes = ['light', 'dark'];
const icons = [
- ,