Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export default defineConfig({
starlight({
title: 'The App Fair Project',
favicon: '/favicon.png',
head: [
{
tag: 'script',
content: `(function(){try{var t=localStorage.getItem('appfair-theme-style');if(t&&t!=='original'){document.documentElement.setAttribute('data-theme-style',t);}}catch(e){}})();`,
},
],
logo: {
src: './public/assets/icons/appfair-icon.svg',
replacesTitle: true,
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions src/components/CustomHeader.astro
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Search from '@astrojs/starlight/components/Search.astro';
import SiteTitle from './SiteTitle.astro';
import SocialIcons from '@astrojs/starlight/components/SocialIcons.astro';
import ThemeSelect from './ThemeSelect.astro';
import ThemeStyleSelect from './ThemeStyleSelect.astro';

const curLocale = Astro.locals.starlightRoute.locale;
const slug = Astro.locals.starlightRoute.entry.slug;
Expand Down Expand Up @@ -63,6 +64,7 @@ const links: Link[] = [
<SocialIcons />
</div>
{/* <LanguageSelect /> — disabled until the docs site has translations to offer */}
<ThemeStyleSelect />
<ThemeSelect />
</div>
</div>
Expand Down
120 changes: 120 additions & 0 deletions src/components/ThemeStyleSelect.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
import { Icon } from '@astrojs/starlight/components';
---

<script>
// Theme palette switcher (independent of dark/light mode)
class ThemeStyleSwitcher extends HTMLElement {
static KEY = 'appfair-theme-style';
static THEMES = [
'original',
'futurism', 'space-age', 'dharma', 'minimalist', 'natural', 'solarpunk',
'brutalist', 'cyberpunk', 'newspaper', 'sumi-e', 'memphis', 'herbarium',
'protest', 'whole-earth', 'syndicalist',
] as const;

connectedCallback() {
const stored = (typeof localStorage !== 'undefined' && localStorage.getItem(ThemeStyleSwitcher.KEY)) || 'original';
this.applyTheme(stored);
const select = this.querySelector('select');
if (select) {
select.value = stored;
select.addEventListener('change', () => {
this.applyTheme(select.value);
localStorage.setItem(ThemeStyleSwitcher.KEY, select.value);
});
}
}

applyTheme(theme: string) {
if (theme === 'original' || !ThemeStyleSwitcher.THEMES.includes(theme as any)) {
document.documentElement.removeAttribute('data-theme-style');
} else {
document.documentElement.setAttribute('data-theme-style', theme);
}
}
}
customElements.define('theme-style-switcher', ThemeStyleSwitcher);
</script>

<theme-style-switcher class="sl-flex">
<label aria-label="Site theme">
<Icon name="seti:image" class="theme-style-icon" />
<select>
<option value="original">Original</option>
<optgroup label="Movements">
<option value="futurism">Italian Futurism</option>
<option value="memphis">Memphis Design</option>
<option value="brutalist">Brutalist Concrete</option>
<option value="minimalist">Minimalist</option>
</optgroup>
<optgroup label="Counterculture">
<option value="protest">60s Protest</option>
<option value="whole-earth">Whole Earth Catalog</option>
<option value="syndicalist">Syndicalist</option>
</optgroup>
<optgroup label="Futures">
<option value="space-age">Space Age</option>
<option value="cyberpunk">Cyberpunk Neon</option>
<option value="solarpunk">Solarpunk</option>
</optgroup>
<optgroup label="Print &amp; ink">
<option value="newspaper">Newspaper Broadsheet</option>
<option value="sumi-e">Sumi-e Ink Wash</option>
<option value="dharma">Dharma Initiative</option>
</optgroup>
<optgroup label="Natural">
<option value="natural">Natural &amp; Serene</option>
<option value="herbarium">Botanical Herbarium</option>
</optgroup>
</select>
<Icon name="down-caret" class="theme-style-caret" />
</label>
</theme-style-switcher>

<style>
theme-style-switcher { align-items: center; }
label {
--sl-style-icon-size: 0.95rem;
--sl-style-caret-size: 1.1rem;
position: relative;
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--sl-color-gray-1);
cursor: pointer;
}
label:hover { color: var(--sl-color-gray-2); }
.theme-style-icon {
font-size: var(--sl-style-icon-size);
position: absolute;
inset-inline-start: 0;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
.theme-style-caret {
font-size: var(--sl-style-caret-size);
position: absolute;
inset-inline-end: 0;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
select {
border: 0;
padding-block: 0.5rem;
padding-inline: calc(var(--sl-style-icon-size) + 0.4rem) calc(var(--sl-style-caret-size) + 0.25rem);
background-color: transparent;
color: inherit;
cursor: pointer;
appearance: none;
font-size: var(--sl-text-sm);
max-width: 11rem;
text-overflow: ellipsis;
}
option {
background-color: var(--sl-color-bg-nav);
color: var(--sl-color-gray-1);
}
</style>
17 changes: 17 additions & 0 deletions src/styles/custom.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@

/* ---- Selectable site themes (loaded before all other rules) ---- */
@import './themes/futurism.css';
@import './themes/space-age.css';
@import './themes/dharma.css';
@import './themes/minimalist.css';
@import './themes/natural.css';
@import './themes/solarpunk.css';
@import './themes/brutalist.css';
@import './themes/cyberpunk.css';
@import './themes/newspaper.css';
@import './themes/sumi-e.css';
@import './themes/memphis.css';
@import './themes/herbarium.css';
@import './themes/protest.css';
@import './themes/whole-earth.css';
@import './themes/syndicalist.css';

/* See theme editor at https://starlight.astro.build/guides/css-and-tailwind/#color-theme-editor */

/* Material Symbols Rounded icon font */
Expand Down
91 changes: 91 additions & 0 deletions src/styles/themes/brutalist.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/* ======================================================================
THEME: Brutalist Concrete
Raw, harsh, mid-century concrete. Pure black, raw concrete grey, hi-vis
orange single accent. Heavy display headings, monospace body. No rounded
corners, dense layouts.
====================================================================== */

@font-face {
font-family: "Archivo Black";
src: url('/assets/fonts/themes/brutalist/archivo-black-400.woff2') format('woff2');
font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
font-family: "Space Mono";
src: url('/assets/fonts/themes/brutalist/space-mono-400.woff2') format('woff2');
font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
font-family: "Space Mono";
src: url('/assets/fonts/themes/brutalist/space-mono-700.woff2') format('woff2');
font-weight: 700; font-style: normal; font-display: swap;
}

/* ---- Dark mode (default) ---- */
:root[data-theme-style="brutalist"] {
--sl-color-black: #000000;
--sl-color-bg: #000000;
--sl-color-bg-nav: #000000;
--sl-color-bg-sidebar: #0a0a0a;
--sl-color-bg-inline-code: #1a1a1a;
--sl-color-white: #f0f0ec;
--sl-color-gray-1: #d4d4d0;
--sl-color-gray-2: #a8a8a4;
--sl-color-gray-3: #7c7c78;
--sl-color-gray-4: #50504c;
--sl-color-gray-5: #2a2a26;
--sl-color-gray-6: #16161a;
--sl-color-gray-7: #0a0a0a;
--sl-color-accent-low: #3a1c00;
--sl-color-accent: #ff6b00;
--sl-color-accent-high: #ffa066;
--sl-color-text: #c4c4c0;
--sl-color-text-accent: #ff6b00;

--theme-font-body: "Space Mono", "JetBrains Mono", "Courier New", ui-monospace, monospace;
--theme-font-heading: "Archivo Black", "Helvetica Black", Impact, sans-serif;
}

/* ---- Light mode ---- */
:root[data-theme="light"][data-theme-style="brutalist"] {
--sl-color-black: #d4d2cb;
--sl-color-bg: #d4d2cb;
--sl-color-bg-nav: #d4d2cb;
--sl-color-bg-sidebar: #c2c0b8;
--sl-color-bg-inline-code: #b0aea5;
--sl-color-white: #000000;
--sl-color-gray-1: #0a0a0a;
--sl-color-gray-2: #1c1c1c;
--sl-color-gray-3: #3a3a36;
--sl-color-gray-4: #5e5e58;
--sl-color-gray-5: #8a8884;
--sl-color-gray-6: #b0aea5;
--sl-color-gray-7: #c2c0b8;
--sl-color-accent-low: #ffd9b8;
--sl-color-accent: #c44a00;
--sl-color-accent-high: #8a3300;
--sl-color-text: #1a1a1a;
--sl-color-text-accent: #c44a00;
}

/* ---- Typography & harsh structural rules ---- */
[data-theme-style="brutalist"] body { font-family: var(--theme-font-body); font-weight: 400; letter-spacing: -0.02em; }
[data-theme-style="brutalist"] h1,
[data-theme-style="brutalist"] h2,
[data-theme-style="brutalist"] h3,
[data-theme-style="brutalist"] h4,
[data-theme-style="brutalist"] h5,
[data-theme-style="brutalist"] h6 {
font-family: var(--theme-font-heading);
font-weight: 400;
text-transform: uppercase;
letter-spacing: -0.01em;
line-height: 1;
}
[data-theme-style="brutalist"] h1 { font-size: 2.6em; }
[data-theme-style="brutalist"] code,
[data-theme-style="brutalist"] pre code,
[data-theme-style="brutalist"] kbd { font-family: var(--theme-font-body); }
[data-theme-style="brutalist"] a { text-decoration: underline; text-underline-offset: 0.2em; text-decoration-thickness: 2px; }
[data-theme-style="brutalist"] hr { border: 0; border-top: 4px solid var(--sl-color-accent); }
[data-theme-style="brutalist"] .site-title { font-family: var(--theme-font-heading); text-transform: uppercase; letter-spacing: 0; }
91 changes: 91 additions & 0 deletions src/styles/themes/cyberpunk.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/* ======================================================================
THEME: Cyberpunk Neon
Vaporwave / Blade Runner. Deep magenta-purple, electric cyan, hot pink.
All-mono typography. Glowing text-shadows. Scan-line feel.
====================================================================== */

@font-face {
font-family: "Major Mono Display";
src: url('/assets/fonts/themes/cyberpunk/major-mono-400.woff2') format('woff2');
font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url('/assets/fonts/themes/cyberpunk/jetbrains-mono-400.woff2') format('woff2');
font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url('/assets/fonts/themes/cyberpunk/jetbrains-mono-700.woff2') format('woff2');
font-weight: 700; font-style: normal; font-display: swap;
}

/* ---- Dark mode (default — the canonical cyberpunk look) ---- */
:root[data-theme-style="cyberpunk"] {
--sl-color-black: #0a0014;
--sl-color-bg: #0a0014;
--sl-color-bg-nav: #0a0014;
--sl-color-bg-sidebar: #100022;
--sl-color-bg-inline-code: #1a0033;
--sl-color-white: #e6f7ff;
--sl-color-gray-1: #b8e3f0;
--sl-color-gray-2: #8a9fd0;
--sl-color-gray-3: #6075a8;
--sl-color-gray-4: #3d4a78;
--sl-color-gray-5: #251847;
--sl-color-gray-6: #1a0033;
--sl-color-gray-7: #100022;
--sl-color-accent-low: #3d0033;
--sl-color-accent: #ff0080;
--sl-color-accent-high: #ff66b3;
--sl-color-text: #b8e3f0;
--sl-color-text-accent: #00ffd5;

--theme-font-body: "JetBrains Mono", "Fira Code", "SF Mono", Menlo, ui-monospace, monospace;
--theme-font-heading: "Major Mono Display", "JetBrains Mono", "Courier New", ui-monospace, monospace;
}

/* ---- Light mode (the daylight-cyberpunk variant) ---- */
:root[data-theme="light"][data-theme-style="cyberpunk"] {
--sl-color-black: #fce6ff;
--sl-color-bg: #fce6ff;
--sl-color-bg-nav: #fce6ff;
--sl-color-bg-sidebar: #f5d6ff;
--sl-color-bg-inline-code: #ebc1f5;
--sl-color-white: #1a0033;
--sl-color-gray-1: #251847;
--sl-color-gray-2: #3d2a6a;
--sl-color-gray-3: #5c468f;
--sl-color-gray-4: #8a73ad;
--sl-color-gray-5: #b8a4cc;
--sl-color-gray-6: #ebc1f5;
--sl-color-gray-7: #f5d6ff;
--sl-color-accent-low: #ffb8db;
--sl-color-accent: #c4006b;
--sl-color-accent-high: #8a004a;
--sl-color-text: #251847;
--sl-color-text-accent: #006b7a;
}

/* ---- Typography & glow effects ---- */
[data-theme-style="cyberpunk"] body { font-family: var(--theme-font-body); font-weight: 400; letter-spacing: 0.02em; }
[data-theme-style="cyberpunk"] h1,
[data-theme-style="cyberpunk"] h2,
[data-theme-style="cyberpunk"] h3,
[data-theme-style="cyberpunk"] h4,
[data-theme-style="cyberpunk"] h5,
[data-theme-style="cyberpunk"] h6 {
font-family: var(--theme-font-heading);
font-weight: 400;
letter-spacing: 0.1em;
text-transform: lowercase;
line-height: 1.25;
}
[data-theme-style="cyberpunk"] h1 { letter-spacing: 0.18em; }
[data-theme-style="cyberpunk"] h2 { letter-spacing: 0.14em; }
[data-theme-style="cyberpunk"] a { text-shadow: 0 0 8px currentColor; }
[data-theme="light"][data-theme-style="cyberpunk"] a { text-shadow: none; }
[data-theme-style="cyberpunk"] code,
[data-theme-style="cyberpunk"] pre code,
[data-theme-style="cyberpunk"] kbd { font-family: var(--theme-font-body); }
[data-theme-style="cyberpunk"] .site-title { font-family: var(--theme-font-heading); letter-spacing: 0.15em; text-transform: lowercase; }
Loading