Complete frontend overhaul with WCAG 2.2 accessibility
Design System: - Custom Hugo theme "swissfini" with editorial aesthetic - CSS custom properties for comprehensive theming - Light, Dark, and High Contrast themes - Print-optimized styles Accessibility Self-Service Controls: - Font size adjustment (5 levels: 75%-150%) - Theme toggle (Light/Dark/High Contrast/System) - Dyslexia-friendly font (OpenDyslexic) - Line spacing control (4 levels) - Reduced motion toggle - Reading width control (3 levels) - Enhanced focus indicators - All preferences persisted via localStorage Templates & Components: - Base layout with skip-links and accessibility panel - Article template with drop caps and blockquotes - Irony box and conclusion shortcodes - Responsive header with mobile navigation Content: - Migrated SCION vs SD-WAN analysis from HTML - Homepage teaser with paywall-style CTA 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
194
themes/swissfini/assets/css/base/_reset.css
Normal file
194
themes/swissfini/assets/css/base/_reset.css
Normal file
@@ -0,0 +1,194 @@
|
||||
/* ============================================
|
||||
Modern CSS Reset
|
||||
Based on Josh Comeau's reset with accessibility enhancements
|
||||
============================================ */
|
||||
|
||||
/* Box sizing */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Remove default margin and padding */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Prevent font size inflation */
|
||||
html {
|
||||
-moz-text-size-adjust: none;
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Smooth scroll only when no preference */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Core body defaults */
|
||||
body {
|
||||
min-height: 100vh;
|
||||
line-height: var(--line-height-body);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--color-text-primary);
|
||||
background-color: var(--color-bg-primary);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
/* Remove list styles on ul, ol with role="list" */
|
||||
ul[role='list'],
|
||||
ol[role='list'] {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* Set shorter line heights on headings and interactive elements */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: var(--line-height-heading);
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/* Cap line length for readability */
|
||||
p, li, figcaption {
|
||||
max-width: var(--reading-width);
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
/* A elements that don't have a class get default styles */
|
||||
a:not([class]) {
|
||||
text-decoration-skip-ink: auto;
|
||||
color: var(--color-link);
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.15em;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
a:not([class]):hover {
|
||||
color: var(--color-link-hover);
|
||||
}
|
||||
|
||||
a:not([class]):visited {
|
||||
color: var(--color-link-visited);
|
||||
}
|
||||
|
||||
/* Make images easier to work with */
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Inherit fonts for inputs and buttons */
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Remove default button styles */
|
||||
button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Textarea without resize (unless specified) */
|
||||
textarea:not([class]) {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* Make sure textareas without a rows attribute are not tiny */
|
||||
textarea:not([rows]) {
|
||||
min-height: 10em;
|
||||
}
|
||||
|
||||
/* Anything that has been anchored to should have extra scroll margin */
|
||||
:target {
|
||||
scroll-margin-block: 5ex;
|
||||
}
|
||||
|
||||
/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Data attribute for user-controlled reduced motion */
|
||||
[data-reduced-motion="true"] *,
|
||||
[data-reduced-motion="true"] *::before,
|
||||
[data-reduced-motion="true"] *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
/* Screen reader only utility */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Focus visible for accessibility */
|
||||
.sr-only:focus-visible {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* Selection styling */
|
||||
::selection {
|
||||
background-color: var(--color-accent-cardinal-subtle);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Focus outline styles */
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
|
||||
outline-offset: var(--focus-outline-offset);
|
||||
}
|
||||
|
||||
/* Enhanced focus mode */
|
||||
[data-enhanced-focus="true"] :focus-visible {
|
||||
outline-width: var(--focus-enhanced-width);
|
||||
outline-offset: var(--focus-enhanced-offset);
|
||||
box-shadow: 0 0 0 calc(var(--focus-enhanced-width) + 2px) var(--color-focus-ring);
|
||||
}
|
||||
391
themes/swissfini/assets/css/base/_typography.css
Normal file
391
themes/swissfini/assets/css/base/_typography.css
Normal file
@@ -0,0 +1,391 @@
|
||||
/* ============================================
|
||||
Typography System
|
||||
Editorial excellence for investigative satire
|
||||
============================================ */
|
||||
|
||||
/* Google Fonts Import */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Literata:ital,opsz,wght@0,7..72,400;0,7..72,500;0,7..72,600;0,7..72,700;1,7..72,400;1,7..72,500&display=swap');
|
||||
|
||||
/* OpenDyslexic Font Face - loaded from CDN */
|
||||
@font-face {
|
||||
font-family: 'OpenDyslexic';
|
||||
src: url('https://cdn.jsdelivr.net/npm/open-dyslexic@1.0.3/woff/OpenDyslexic-Regular.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenDyslexic';
|
||||
src: url('https://cdn.jsdelivr.net/npm/open-dyslexic@1.0.3/woff/OpenDyslexic-Bold.woff') format('woff');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'OpenDyslexic';
|
||||
src: url('https://cdn.jsdelivr.net/npm/open-dyslexic@1.0.3/woff/OpenDyslexic-Italic.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
HEADINGS
|
||||
======================================== */
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-body);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-text-primary);
|
||||
letter-spacing: var(--letter-spacing-tight);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--font-size-4xl);
|
||||
line-height: 1.1;
|
||||
letter-spacing: var(--letter-spacing-tighter);
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--font-size-2xl);
|
||||
line-height: 1.2;
|
||||
margin-top: var(--space-12);
|
||||
margin-bottom: var(--space-4);
|
||||
padding-bottom: var(--space-2);
|
||||
border-bottom: var(--border-width-thick) solid var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: var(--font-size-xl);
|
||||
line-height: 1.3;
|
||||
margin-top: var(--space-8);
|
||||
color: var(--color-accent-navy);
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: var(--font-size-lg);
|
||||
line-height: 1.4;
|
||||
margin-top: var(--space-6);
|
||||
}
|
||||
|
||||
h5, h6 {
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--letter-spacing-wide);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
BODY TEXT
|
||||
======================================== */
|
||||
|
||||
p {
|
||||
font-size: var(--font-size-md);
|
||||
line-height: var(--line-height-body);
|
||||
letter-spacing: var(--letter-spacing-body);
|
||||
word-spacing: var(--word-spacing-body);
|
||||
margin-bottom: var(--space-6);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Lead paragraph */
|
||||
.lead,
|
||||
.article-body > p:first-of-type {
|
||||
font-size: var(--font-size-lg);
|
||||
line-height: 1.7;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Small text */
|
||||
small,
|
||||
.text-small {
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
DROP CAP - Vintage newspaper style
|
||||
======================================== */
|
||||
|
||||
.drop-cap::first-letter,
|
||||
.article-body > p:first-of-type::first-letter {
|
||||
float: left;
|
||||
font-size: 4.5em;
|
||||
line-height: 0.8;
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-accent-cardinal);
|
||||
margin-right: var(--space-3);
|
||||
margin-top: 0.1em;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
LINKS
|
||||
======================================== */
|
||||
|
||||
a {
|
||||
color: var(--color-link);
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.2em;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-link-hover);
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
|
||||
a:focus-visible {
|
||||
outline: var(--focus-outline-width) var(--focus-outline-style) var(--color-focus);
|
||||
outline-offset: var(--focus-outline-offset);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
LISTS
|
||||
======================================== */
|
||||
|
||||
ul, ol {
|
||||
margin-bottom: var(--space-6);
|
||||
padding-left: var(--space-6);
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: var(--font-size-md);
|
||||
line-height: var(--line-height-body);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
li::marker {
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
/* Nested lists */
|
||||
li > ul,
|
||||
li > ol {
|
||||
margin-top: var(--space-2);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
BLOCKQUOTES - Editorial citations
|
||||
======================================== */
|
||||
|
||||
blockquote {
|
||||
position: relative;
|
||||
margin: var(--space-8) 0;
|
||||
padding: var(--space-6) var(--space-8);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--color-accent-gold-subtle) 0%,
|
||||
transparent 50%
|
||||
);
|
||||
border-left: var(--border-width-heavy) solid var(--color-accent-gold);
|
||||
border-radius: 0 var(--radius-md) var(--radius-md) 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
blockquote::before {
|
||||
content: '"';
|
||||
position: absolute;
|
||||
top: var(--space-2);
|
||||
left: var(--space-3);
|
||||
font-size: var(--font-size-4xl);
|
||||
font-family: var(--font-serif);
|
||||
color: var(--color-accent-gold);
|
||||
opacity: 0.4;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
blockquote p {
|
||||
font-size: var(--font-size-md);
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
blockquote p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
blockquote cite,
|
||||
blockquote .cite {
|
||||
display: block;
|
||||
margin-top: var(--space-4);
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
blockquote cite::before,
|
||||
blockquote .cite::before {
|
||||
content: '— ';
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
CODE
|
||||
======================================== */
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.9em;
|
||||
background-color: var(--color-bg-secondary);
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: var(--space-6) 0;
|
||||
padding: var(--space-6);
|
||||
background-color: var(--color-accent-navy);
|
||||
border-radius: var(--radius-md);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: var(--color-text-inverse);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
HORIZONTAL RULE
|
||||
======================================== */
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
height: var(--border-width);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
var(--color-border-strong) 20%,
|
||||
var(--color-border-strong) 80%,
|
||||
transparent
|
||||
);
|
||||
margin: var(--space-12) 0;
|
||||
}
|
||||
|
||||
/* Decorative rule with diamond */
|
||||
hr.ornament {
|
||||
position: relative;
|
||||
background: none;
|
||||
height: var(--space-6);
|
||||
}
|
||||
|
||||
hr.ornament::before {
|
||||
content: '◆';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
hr.ornament::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
var(--color-border-strong) 15%,
|
||||
transparent 45%,
|
||||
transparent 55%,
|
||||
var(--color-border-strong) 85%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
EMPHASIS & STRONG
|
||||
======================================== */
|
||||
|
||||
strong, b {
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
em, i {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: var(--color-accent-gold-subtle);
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
DYSLEXIA MODE OVERRIDES
|
||||
======================================== */
|
||||
|
||||
[data-dyslexia="true"] {
|
||||
--font-body: var(--font-dyslexia);
|
||||
--letter-spacing-body: var(--letter-spacing-dyslexia);
|
||||
--word-spacing-body: var(--word-spacing-dyslexia);
|
||||
}
|
||||
|
||||
[data-dyslexia="true"] h1,
|
||||
[data-dyslexia="true"] h2,
|
||||
[data-dyslexia="true"] h3,
|
||||
[data-dyslexia="true"] h4,
|
||||
[data-dyslexia="true"] h5,
|
||||
[data-dyslexia="true"] h6 {
|
||||
font-family: var(--font-dyslexia);
|
||||
letter-spacing: var(--letter-spacing-dyslexia);
|
||||
}
|
||||
|
||||
[data-dyslexia="true"] .drop-cap::first-letter,
|
||||
[data-dyslexia="true"] .article-body > p:first-of-type::first-letter {
|
||||
font-family: var(--font-dyslexia);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
LINE SPACING ADJUSTMENTS
|
||||
======================================== */
|
||||
|
||||
[data-line-spacing="compact"] {
|
||||
--line-height-body: var(--line-height-snug);
|
||||
}
|
||||
|
||||
[data-line-spacing="normal"] {
|
||||
--line-height-body: var(--line-height-normal);
|
||||
}
|
||||
|
||||
[data-line-spacing="relaxed"] {
|
||||
--line-height-body: var(--line-height-relaxed);
|
||||
}
|
||||
|
||||
[data-line-spacing="loose"] {
|
||||
--line-height-body: var(--line-height-loose);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
READING WIDTH ADJUSTMENTS
|
||||
======================================== */
|
||||
|
||||
[data-reading-width="narrow"] {
|
||||
--reading-width: var(--reading-width-narrow);
|
||||
}
|
||||
|
||||
[data-reading-width="medium"] {
|
||||
--reading-width: var(--reading-width-medium);
|
||||
}
|
||||
|
||||
[data-reading-width="wide"] {
|
||||
--reading-width: var(--reading-width-wide);
|
||||
}
|
||||
231
themes/swissfini/assets/css/base/_variables.css
Normal file
231
themes/swissfini/assets/css/base/_variables.css
Normal file
@@ -0,0 +1,231 @@
|
||||
/* ============================================
|
||||
SwissFini.sh Design System
|
||||
"Precision Satire" - Editorial Authority with Swiss Irony
|
||||
============================================ */
|
||||
|
||||
:root {
|
||||
/* ========================================
|
||||
COLOR PALETTE - LIGHT THEME (DEFAULT)
|
||||
Inspired by vintage investigative journalism
|
||||
======================================== */
|
||||
|
||||
/* Paper & Background */
|
||||
--color-bg-primary: #faf9f7;
|
||||
--color-bg-secondary: #f5f3f0;
|
||||
--color-bg-tertiary: #ebe8e4;
|
||||
--color-bg-elevated: #ffffff;
|
||||
|
||||
/* Text Hierarchy */
|
||||
--color-text-primary: #1a1a1a;
|
||||
--color-text-secondary: #4a4a4a;
|
||||
--color-text-tertiary: #6b6b6b;
|
||||
--color-text-muted: #8a8a8a;
|
||||
--color-text-inverse: #faf9f7;
|
||||
|
||||
/* Editorial Accents */
|
||||
--color-accent-cardinal: #c41e3a;
|
||||
--color-accent-cardinal-hover: #a31830;
|
||||
--color-accent-cardinal-subtle: rgba(196, 30, 58, 0.1);
|
||||
|
||||
--color-accent-navy: #1e3a5f;
|
||||
--color-accent-navy-hover: #152a45;
|
||||
--color-accent-navy-subtle: rgba(30, 58, 95, 0.08);
|
||||
|
||||
--color-accent-gold: #c9a227;
|
||||
--color-accent-gold-subtle: rgba(201, 162, 39, 0.15);
|
||||
|
||||
/* Semantic Colors */
|
||||
--color-link: #1e3a5f;
|
||||
--color-link-hover: #c41e3a;
|
||||
--color-link-visited: #5a3d6b;
|
||||
|
||||
--color-success: #2d6a4f;
|
||||
--color-warning: #b45309;
|
||||
--color-error: #c41e3a;
|
||||
|
||||
/* Focus & Accessibility */
|
||||
--color-focus: #0066cc;
|
||||
--color-focus-ring: rgba(0, 102, 204, 0.35);
|
||||
--color-focus-visible: #0052a3;
|
||||
|
||||
/* Borders & Dividers */
|
||||
--color-border: #e5e2de;
|
||||
--color-border-strong: #d0ccc6;
|
||||
--color-divider: #ebe8e4;
|
||||
|
||||
/* ========================================
|
||||
TYPOGRAPHY
|
||||
======================================== */
|
||||
|
||||
/* Font Stacks */
|
||||
--font-serif: 'Literata', 'Georgia', 'Times New Roman', Times, serif;
|
||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
--font-dyslexia: 'OpenDyslexic', 'Comic Sans MS', 'Arial', sans-serif;
|
||||
|
||||
/* Active Font Family (swapped via JS) */
|
||||
--font-body: var(--font-serif);
|
||||
--font-ui: var(--font-sans);
|
||||
|
||||
/* Font Size Scale (multiplied by --font-size-scale) */
|
||||
--font-size-scale: 1;
|
||||
|
||||
--font-size-2xs: calc(0.625rem * var(--font-size-scale)); /* 10px */
|
||||
--font-size-xs: calc(0.75rem * var(--font-size-scale)); /* 12px */
|
||||
--font-size-sm: calc(0.875rem * var(--font-size-scale)); /* 14px */
|
||||
--font-size-base: calc(1rem * var(--font-size-scale)); /* 16px */
|
||||
--font-size-md: calc(1.125rem * var(--font-size-scale)); /* 18px */
|
||||
--font-size-lg: calc(1.25rem * var(--font-size-scale)); /* 20px */
|
||||
--font-size-xl: calc(1.5rem * var(--font-size-scale)); /* 24px */
|
||||
--font-size-2xl: calc(1.875rem * var(--font-size-scale)); /* 30px */
|
||||
--font-size-3xl: calc(2.25rem * var(--font-size-scale)); /* 36px */
|
||||
--font-size-4xl: calc(3rem * var(--font-size-scale)); /* 48px */
|
||||
--font-size-5xl: calc(3.75rem * var(--font-size-scale)); /* 60px */
|
||||
|
||||
/* Font Weights */
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
--font-weight-black: 900;
|
||||
|
||||
/* Line Heights (adjustable via accessibility) */
|
||||
--line-height-tight: 1.25;
|
||||
--line-height-snug: 1.375;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-relaxed: 1.75;
|
||||
--line-height-loose: 2;
|
||||
|
||||
--line-height-body: var(--line-height-relaxed);
|
||||
--line-height-heading: var(--line-height-snug);
|
||||
|
||||
/* Letter Spacing */
|
||||
--letter-spacing-tighter: -0.05em;
|
||||
--letter-spacing-tight: -0.025em;
|
||||
--letter-spacing-normal: 0;
|
||||
--letter-spacing-wide: 0.025em;
|
||||
--letter-spacing-wider: 0.05em;
|
||||
--letter-spacing-widest: 0.1em;
|
||||
|
||||
/* For dyslexia mode */
|
||||
--letter-spacing-dyslexia: 0.35ch;
|
||||
--word-spacing-dyslexia: 1.225ch;
|
||||
|
||||
--letter-spacing-body: var(--letter-spacing-normal);
|
||||
--word-spacing-body: normal;
|
||||
|
||||
/* ========================================
|
||||
SPACING SCALE
|
||||
======================================== */
|
||||
|
||||
--space-0: 0;
|
||||
--space-1: 0.25rem; /* 4px */
|
||||
--space-2: 0.5rem; /* 8px */
|
||||
--space-3: 0.75rem; /* 12px */
|
||||
--space-4: 1rem; /* 16px */
|
||||
--space-5: 1.25rem; /* 20px */
|
||||
--space-6: 1.5rem; /* 24px */
|
||||
--space-8: 2rem; /* 32px */
|
||||
--space-10: 2.5rem; /* 40px */
|
||||
--space-12: 3rem; /* 48px */
|
||||
--space-16: 4rem; /* 64px */
|
||||
--space-20: 5rem; /* 80px */
|
||||
--space-24: 6rem; /* 96px */
|
||||
--space-32: 8rem; /* 128px */
|
||||
|
||||
/* ========================================
|
||||
LAYOUT
|
||||
======================================== */
|
||||
|
||||
/* Reading Width (adjustable) */
|
||||
--reading-width-narrow: 55ch;
|
||||
--reading-width-medium: 70ch;
|
||||
--reading-width-wide: 85ch;
|
||||
--reading-width: var(--reading-width-medium);
|
||||
|
||||
/* Container */
|
||||
--container-max: 1200px;
|
||||
--container-padding: var(--space-6);
|
||||
|
||||
/* Article Width */
|
||||
--article-width: min(var(--reading-width), 100%);
|
||||
|
||||
/* ========================================
|
||||
BORDERS & EFFECTS
|
||||
======================================== */
|
||||
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
--radius-full: 9999px;
|
||||
|
||||
--border-width: 1px;
|
||||
--border-width-thick: 2px;
|
||||
--border-width-heavy: 4px;
|
||||
|
||||
/* Shadows - Editorial depth */
|
||||
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.04);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -2px rgba(0, 0, 0, 0.04);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
|
||||
/* Elevated surface shadow */
|
||||
--shadow-elevated:
|
||||
0 0 0 1px rgba(0, 0, 0, 0.03),
|
||||
0 2px 4px rgba(0, 0, 0, 0.04),
|
||||
0 8px 16px rgba(0, 0, 0, 0.06);
|
||||
|
||||
/* ========================================
|
||||
FOCUS INDICATORS (WCAG 2.2)
|
||||
======================================== */
|
||||
|
||||
--focus-outline-width: 3px;
|
||||
--focus-outline-offset: 2px;
|
||||
--focus-outline-style: solid;
|
||||
--focus-outline-color: var(--color-focus);
|
||||
|
||||
/* Enhanced focus (accessibility toggle) */
|
||||
--focus-enhanced-width: 4px;
|
||||
--focus-enhanced-offset: 4px;
|
||||
|
||||
/* ========================================
|
||||
TRANSITIONS
|
||||
======================================== */
|
||||
|
||||
--duration-instant: 0ms;
|
||||
--duration-fast: 150ms;
|
||||
--duration-normal: 250ms;
|
||||
--duration-slow: 350ms;
|
||||
--duration-slower: 500ms;
|
||||
|
||||
--ease-default: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
|
||||
--transition-colors: color var(--duration-fast) var(--ease-default),
|
||||
background-color var(--duration-fast) var(--ease-default),
|
||||
border-color var(--duration-fast) var(--ease-default);
|
||||
--transition-transform: transform var(--duration-normal) var(--ease-out);
|
||||
--transition-opacity: opacity var(--duration-normal) var(--ease-default);
|
||||
--transition-shadow: box-shadow var(--duration-normal) var(--ease-default);
|
||||
|
||||
/* ========================================
|
||||
Z-INDEX SCALE
|
||||
======================================== */
|
||||
|
||||
--z-base: 0;
|
||||
--z-dropdown: 100;
|
||||
--z-sticky: 200;
|
||||
--z-fixed: 300;
|
||||
--z-modal-backdrop: 400;
|
||||
--z-modal: 500;
|
||||
--z-popover: 600;
|
||||
--z-tooltip: 700;
|
||||
--z-accessibility-panel: 800;
|
||||
--z-skip-link: 900;
|
||||
--z-max: 9999;
|
||||
}
|
||||
400
themes/swissfini/assets/css/components/_accessibility-panel.css
Normal file
400
themes/swissfini/assets/css/components/_accessibility-panel.css
Normal file
@@ -0,0 +1,400 @@
|
||||
/* ============================================
|
||||
Accessibility Panel
|
||||
First-class citizen for user autonomy
|
||||
============================================ */
|
||||
|
||||
/* ========================================
|
||||
Toggle Button
|
||||
======================================== */
|
||||
|
||||
.accessibility-toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: var(--border-width) solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: var(--transition-colors), var(--transition-transform);
|
||||
}
|
||||
|
||||
.accessibility-toggle:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
color: var(--color-text-primary);
|
||||
border-color: var(--color-border-strong);
|
||||
}
|
||||
|
||||
.accessibility-toggle:focus-visible {
|
||||
outline: var(--focus-outline-width) var(--focus-outline-style) var(--color-focus);
|
||||
outline-offset: var(--focus-outline-offset);
|
||||
}
|
||||
|
||||
.accessibility-toggle[aria-expanded="true"] {
|
||||
background-color: var(--color-accent-navy);
|
||||
color: var(--color-text-inverse);
|
||||
border-color: var(--color-accent-navy);
|
||||
}
|
||||
|
||||
.accessibility-toggle svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Pulse animation for discoverability (first visit) */
|
||||
@keyframes pulse-ring {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(196, 30, 58, 0.4);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 10px rgba(196, 30, 58, 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(196, 30, 58, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.accessibility-toggle.is-new {
|
||||
animation: pulse-ring 2s ease-out infinite;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Panel Container
|
||||
======================================== */
|
||||
|
||||
.accessibility-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: var(--z-accessibility-panel);
|
||||
width: min(400px, 100vw);
|
||||
height: 100vh;
|
||||
background-color: var(--color-bg-elevated);
|
||||
border-left: var(--border-width) solid var(--color-border);
|
||||
box-shadow: var(--shadow-xl);
|
||||
transform: translateX(100%);
|
||||
transition: transform var(--duration-slow) var(--ease-out),
|
||||
visibility var(--duration-slow);
|
||||
visibility: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.accessibility-panel.is-open {
|
||||
transform: translateX(0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Backdrop */
|
||||
.accessibility-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: calc(var(--z-accessibility-panel) - 1);
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity var(--duration-normal) var(--ease-default),
|
||||
visibility var(--duration-normal);
|
||||
}
|
||||
|
||||
.accessibility-backdrop.is-visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .accessibility-backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Panel Header
|
||||
======================================== */
|
||||
|
||||
.accessibility-panel__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--space-5) var(--space-6);
|
||||
border-bottom: var(--border-width) solid var(--color-border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: var(--color-bg-elevated);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.accessibility-panel__header h2 {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
margin: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.accessibility-panel__close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--color-text-tertiary);
|
||||
cursor: pointer;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
.accessibility-panel__close:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.accessibility-panel__close:focus-visible {
|
||||
outline: var(--focus-outline-width) var(--focus-outline-style) var(--color-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.accessibility-panel__close svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Panel Content
|
||||
======================================== */
|
||||
|
||||
.accessibility-panel__content {
|
||||
padding: var(--space-6);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Control Groups
|
||||
======================================== */
|
||||
|
||||
.accessibility-control {
|
||||
margin-bottom: var(--space-6);
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.accessibility-control legend {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--letter-spacing-wide);
|
||||
margin-bottom: var(--space-3);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.accessibility-control__buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.accessibility-control__value {
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-text-primary);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background-color: var(--color-bg-tertiary);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Button Styles
|
||||
======================================== */
|
||||
|
||||
.btn {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
border-radius: var(--radius-sm);
|
||||
border: var(--border-width) solid var(--color-border);
|
||||
background-color: var(--color-bg-secondary);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
border-color: var(--color-border-strong);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.btn:focus-visible {
|
||||
outline: var(--focus-outline-width) var(--focus-outline-style) var(--color-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Icon button (font size controls) */
|
||||
.btn--icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.btn--icon:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Option buttons (segmented control) */
|
||||
.btn--option {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
text-align: center;
|
||||
padding: var(--space-3) var(--space-2);
|
||||
}
|
||||
|
||||
.btn--option[aria-pressed="true"] {
|
||||
background-color: var(--color-accent-navy);
|
||||
border-color: var(--color-accent-navy);
|
||||
color: var(--color-text-inverse);
|
||||
}
|
||||
|
||||
/* Toggle button (switch style) */
|
||||
.btn--toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: var(--space-3) var(--space-4);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.btn--toggle .btn__label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.btn--toggle .btn__state {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
background-color: var(--color-bg-tertiary);
|
||||
border-radius: var(--radius-full);
|
||||
transition: background-color var(--duration-fast) var(--ease-default);
|
||||
}
|
||||
|
||||
.btn--toggle .btn__state::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: var(--color-bg-elevated);
|
||||
border-radius: var(--radius-full);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform var(--duration-fast) var(--ease-default);
|
||||
}
|
||||
|
||||
.btn--toggle[aria-pressed="true"] .btn__state {
|
||||
background-color: var(--color-accent-navy);
|
||||
}
|
||||
|
||||
.btn--toggle[aria-pressed="true"] .btn__state::after {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/* Secondary button (reset) */
|
||||
.btn--secondary {
|
||||
background-color: transparent;
|
||||
border-color: var(--color-accent-cardinal);
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
.btn--secondary:hover {
|
||||
background-color: var(--color-accent-cardinal);
|
||||
color: var(--color-text-inverse);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Footer
|
||||
======================================== */
|
||||
|
||||
.accessibility-control--footer {
|
||||
margin-top: var(--space-8);
|
||||
padding-top: var(--space-6);
|
||||
border-top: var(--border-width) solid var(--color-border);
|
||||
}
|
||||
|
||||
.accessibility-control--footer .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Live Region (Screen Reader Announcements)
|
||||
======================================== */
|
||||
|
||||
.accessibility-announcer {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Reduced Motion Styles
|
||||
======================================== */
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.accessibility-panel {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.accessibility-backdrop {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.accessibility-toggle.is-new {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-reduced-motion="true"] .accessibility-panel {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
[data-reduced-motion="true"] .accessibility-backdrop {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
[data-reduced-motion="true"] .accessibility-toggle.is-new {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Mobile Adjustments
|
||||
======================================== */
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.accessibility-panel {
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.btn--option {
|
||||
font-size: var(--font-size-xs);
|
||||
padding: var(--space-2) var(--space-1);
|
||||
}
|
||||
}
|
||||
413
themes/swissfini/assets/css/components/_article.css
Normal file
413
themes/swissfini/assets/css/components/_article.css
Normal file
@@ -0,0 +1,413 @@
|
||||
/* ============================================
|
||||
Article Styles
|
||||
Long-form investigative journalism layout
|
||||
============================================ */
|
||||
|
||||
.article {
|
||||
max-width: var(--article-width);
|
||||
margin: 0 auto;
|
||||
padding: var(--space-8) var(--container-padding) var(--space-16);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Article Header
|
||||
======================================== */
|
||||
|
||||
.article-header {
|
||||
margin-bottom: var(--space-10);
|
||||
padding-bottom: var(--space-8);
|
||||
border-bottom: var(--border-width) solid var(--color-border);
|
||||
}
|
||||
|
||||
.article-category {
|
||||
display: inline-block;
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-bold);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--letter-spacing-widest);
|
||||
color: var(--color-accent-cardinal);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.article-title {
|
||||
font-size: clamp(var(--font-size-2xl), 5vw, var(--font-size-4xl));
|
||||
line-height: 1.1;
|
||||
margin-bottom: var(--space-4);
|
||||
letter-spacing: var(--letter-spacing-tighter);
|
||||
}
|
||||
|
||||
.article-subtitle {
|
||||
font-size: var(--font-size-lg);
|
||||
font-style: italic;
|
||||
color: var(--color-text-secondary);
|
||||
line-height: var(--line-height-relaxed);
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-4);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
.article-meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.article-meta-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Article Body
|
||||
======================================== */
|
||||
|
||||
.article-body {
|
||||
font-size: var(--font-size-md);
|
||||
line-height: var(--line-height-body);
|
||||
}
|
||||
|
||||
.article-body > * {
|
||||
max-width: var(--reading-width);
|
||||
}
|
||||
|
||||
.article-body > h2 {
|
||||
margin-top: var(--space-16);
|
||||
}
|
||||
|
||||
.article-body > h3 {
|
||||
margin-top: var(--space-10);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Tables - Comparison / Data Tables
|
||||
======================================== */
|
||||
|
||||
.article-body table,
|
||||
.comparison-table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: var(--space-8) 0;
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
background-color: var(--color-bg-elevated);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.article-body th,
|
||||
.comparison-table th {
|
||||
background-color: var(--color-accent-navy);
|
||||
color: var(--color-text-inverse);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
text-align: left;
|
||||
padding: var(--space-4) var(--space-5);
|
||||
border-bottom: var(--border-width-thick) solid var(--color-accent-navy-hover);
|
||||
}
|
||||
|
||||
.article-body th:first-child,
|
||||
.comparison-table th:first-child {
|
||||
border-radius: var(--radius-md) 0 0 0;
|
||||
}
|
||||
|
||||
.article-body th:last-child,
|
||||
.comparison-table th:last-child {
|
||||
border-radius: 0 var(--radius-md) 0 0;
|
||||
}
|
||||
|
||||
.article-body td,
|
||||
.comparison-table td {
|
||||
padding: var(--space-4) var(--space-5);
|
||||
border-bottom: var(--border-width) solid var(--color-border);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.article-body tr:last-child td,
|
||||
.comparison-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.article-body tr:hover,
|
||||
.comparison-table tr:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
}
|
||||
|
||||
/* Metric column (first column) */
|
||||
.metric-col {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-text-secondary);
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
/* SCION column - Cardinal red */
|
||||
.scion-col {
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
/* SD-WAN column - Success green */
|
||||
.sdwan-col {
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Irony Box - Official Warning (But Absurd)
|
||||
======================================== */
|
||||
|
||||
.irony-box {
|
||||
position: relative;
|
||||
margin: var(--space-8) 0;
|
||||
padding: var(--space-6) var(--space-6) var(--space-6) var(--space-8);
|
||||
background-color: rgba(196, 30, 58, 0.05);
|
||||
border-left: var(--border-width-heavy) solid var(--color-accent-cardinal);
|
||||
border-radius: 0 var(--radius-md) var(--radius-md) 0;
|
||||
}
|
||||
|
||||
.irony-box::before {
|
||||
content: '⚠';
|
||||
position: absolute;
|
||||
top: var(--space-4);
|
||||
left: calc(var(--space-8) * -1 - 1rem);
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--color-accent-cardinal);
|
||||
color: var(--color-text-inverse);
|
||||
font-size: var(--font-size-sm);
|
||||
border-radius: var(--radius-full);
|
||||
}
|
||||
|
||||
.irony-box strong {
|
||||
color: var(--color-accent-cardinal);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.irony-box p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Dark mode adjustment */
|
||||
[data-theme="dark"] .irony-box {
|
||||
background-color: rgba(232, 74, 100, 0.08);
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] .irony-box {
|
||||
background-color: rgba(255, 77, 109, 0.15);
|
||||
border-left-width: 5px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Conclusion Box
|
||||
======================================== */
|
||||
|
||||
.conclusion {
|
||||
margin: var(--space-10) 0;
|
||||
padding: var(--space-8);
|
||||
background-color: var(--color-accent-navy);
|
||||
color: var(--color-text-inverse);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.conclusion h3 {
|
||||
color: var(--color-accent-gold);
|
||||
margin-top: 0;
|
||||
margin-bottom: var(--space-4);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.conclusion p {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.conclusion ul {
|
||||
margin: var(--space-4) 0;
|
||||
padding-left: var(--space-6);
|
||||
}
|
||||
|
||||
.conclusion li {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.conclusion li::marker {
|
||||
color: var(--color-accent-gold);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Sources Section
|
||||
======================================== */
|
||||
|
||||
.sources {
|
||||
margin-top: var(--space-12);
|
||||
padding: var(--space-6);
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.sources h3 {
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--color-text-secondary);
|
||||
margin-top: 0;
|
||||
margin-bottom: var(--space-4);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.sources ul {
|
||||
margin: 0;
|
||||
padding-left: var(--space-5);
|
||||
}
|
||||
|
||||
.sources li {
|
||||
margin-bottom: var(--space-2);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: var(--line-height-normal);
|
||||
}
|
||||
|
||||
.sources a {
|
||||
color: var(--color-link);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Fade Overlay (Paywall Teaser)
|
||||
======================================== */
|
||||
|
||||
.fade-overlay {
|
||||
position: relative;
|
||||
margin-top: calc(var(--space-16) * -1);
|
||||
padding-top: var(--space-16);
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
var(--color-bg-primary) 40%,
|
||||
var(--color-bg-primary) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.paywall-box {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
var(--color-accent-navy) 0%,
|
||||
#2d3a5f 100%
|
||||
);
|
||||
color: var(--color-text-inverse);
|
||||
padding: var(--space-10);
|
||||
border-radius: var(--radius-lg);
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
.paywall-box h2 {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-xl);
|
||||
color: white;
|
||||
margin: 0 0 var(--space-4);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.paywall-box > p {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-base);
|
||||
margin-bottom: var(--space-6);
|
||||
opacity: 0.9;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.teaser-list {
|
||||
text-align: left;
|
||||
max-width: 400px;
|
||||
margin: var(--space-6) auto;
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
padding-left: var(--space-6);
|
||||
}
|
||||
|
||||
.teaser-list li {
|
||||
margin-bottom: var(--space-3);
|
||||
opacity: 0.9;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.teaser-list li::marker {
|
||||
color: var(--color-accent-gold);
|
||||
}
|
||||
|
||||
/* CTA Button */
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
background-color: var(--color-accent-cardinal);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: var(--space-4) var(--space-8);
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
transition: background-color var(--duration-fast) var(--ease-default),
|
||||
transform var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
background-color: var(--color-accent-cardinal-hover);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.cta-button:focus-visible {
|
||||
outline: 3px solid var(--color-accent-gold);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.free-label {
|
||||
display: block;
|
||||
margin-top: var(--space-4);
|
||||
font-size: var(--font-size-sm);
|
||||
opacity: 0.7;
|
||||
font-family: var(--font-ui);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Responsive
|
||||
======================================== */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.article {
|
||||
padding: var(--space-6) var(--space-4) var(--space-12);
|
||||
}
|
||||
|
||||
.article-title {
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
|
||||
.article-body table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.irony-box {
|
||||
padding-left: var(--space-6);
|
||||
}
|
||||
|
||||
.irony-box::before {
|
||||
position: static;
|
||||
display: inline-flex;
|
||||
margin-right: var(--space-2);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
}
|
||||
153
themes/swissfini/assets/css/components/_footer.css
Normal file
153
themes/swissfini/assets/css/components/_footer.css
Normal file
@@ -0,0 +1,153 @@
|
||||
/* ============================================
|
||||
Site Footer
|
||||
Clean, informative, accessible
|
||||
============================================ */
|
||||
|
||||
.site-footer {
|
||||
background-color: var(--color-accent-navy);
|
||||
color: var(--color-text-inverse);
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
max-width: var(--container-max);
|
||||
margin: 0 auto;
|
||||
padding: var(--space-12) var(--container-padding) var(--space-8);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Footer Top
|
||||
======================================== */
|
||||
|
||||
.footer-top {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr;
|
||||
gap: var(--space-10);
|
||||
padding-bottom: var(--space-10);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.footer-brand {
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.footer-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
text-decoration: none;
|
||||
color: var(--color-text-inverse);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.footer-logo .logo-mark {
|
||||
background-color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
.footer-logo .site-title {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
.footer-description {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
line-height: var(--line-height-relaxed);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Footer Sections */
|
||||
.footer-section h3 {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-bold);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--letter-spacing-widest);
|
||||
color: var(--color-accent-gold);
|
||||
margin-bottom: var(--space-4);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-decoration: none;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: var(--color-text-inverse);
|
||||
}
|
||||
|
||||
.footer-links a:focus-visible {
|
||||
outline: 2px solid var(--color-accent-gold);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Footer Bottom
|
||||
======================================== */
|
||||
|
||||
.footer-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: var(--space-6);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-xs);
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.footer-copyright {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer-legal {
|
||||
display: flex;
|
||||
gap: var(--space-4);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.footer-legal a {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
text-decoration: none;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
.footer-legal a:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Responsive
|
||||
======================================== */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-top {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
.footer-brand {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
258
themes/swissfini/assets/css/components/_header.css
Normal file
258
themes/swissfini/assets/css/components/_header.css
Normal file
@@ -0,0 +1,258 @@
|
||||
/* ============================================
|
||||
Site Header
|
||||
Authoritative yet approachable editorial header
|
||||
============================================ */
|
||||
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: var(--z-sticky);
|
||||
background-color: var(--color-bg-primary);
|
||||
border-bottom: var(--border-width) solid var(--color-border);
|
||||
transition: var(--transition-shadow), var(--transition-colors);
|
||||
}
|
||||
|
||||
.site-header.scrolled {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: var(--container-max);
|
||||
margin: 0 auto;
|
||||
padding: var(--space-4) var(--container-padding);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Logo / Site Title
|
||||
======================================== */
|
||||
|
||||
.site-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
text-decoration: none;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.site-logo:hover {
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
.logo-mark {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--color-accent-cardinal);
|
||||
color: var(--color-text-inverse);
|
||||
font-family: var(--font-ui);
|
||||
font-weight: var(--font-weight-black);
|
||||
font-size: var(--font-size-lg);
|
||||
border-radius: var(--radius-sm);
|
||||
transition: var(--transition-transform);
|
||||
}
|
||||
|
||||
.site-logo:hover .logo-mark {
|
||||
transform: rotate(-3deg) scale(1.05);
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--font-size-xl);
|
||||
font-weight: var(--font-weight-bold);
|
||||
letter-spacing: var(--letter-spacing-tight);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.site-title .swiss {
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
.site-tagline {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-tertiary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--letter-spacing-widest);
|
||||
margin-top: var(--space-1);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Navigation
|
||||
======================================== */
|
||||
|
||||
.main-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
|
||||
.nav-list {
|
||||
display: flex;
|
||||
gap: var(--space-6);
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-text-secondary);
|
||||
text-decoration: none;
|
||||
padding: var(--space-2) var(--space-1);
|
||||
position: relative;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
.nav-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background-color: var(--color-accent-cardinal);
|
||||
transform: scaleX(0);
|
||||
transform-origin: right;
|
||||
transition: transform var(--duration-normal) var(--ease-out);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.nav-link:hover::after,
|
||||
.nav-link.active::after {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Header Actions (Accessibility Toggle)
|
||||
======================================== */
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Mobile Navigation
|
||||
======================================== */
|
||||
|
||||
.nav-toggle {
|
||||
display: none;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.nav-toggle-icon {
|
||||
width: 24px;
|
||||
height: 2px;
|
||||
background-color: currentColor;
|
||||
position: relative;
|
||||
transition: var(--transition-colors);
|
||||
}
|
||||
|
||||
.nav-toggle-icon::before,
|
||||
.nav-toggle-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: currentColor;
|
||||
transition: transform var(--duration-normal) var(--ease-default);
|
||||
}
|
||||
|
||||
.nav-toggle-icon::before {
|
||||
top: -8px;
|
||||
}
|
||||
|
||||
.nav-toggle-icon::after {
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
/* Open state */
|
||||
.nav-toggle[aria-expanded="true"] .nav-toggle-icon {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.nav-toggle[aria-expanded="true"] .nav-toggle-icon::before {
|
||||
transform: rotate(45deg) translate(5px, 6px);
|
||||
}
|
||||
|
||||
.nav-toggle[aria-expanded="true"] .nav-toggle-icon::after {
|
||||
transform: rotate(-45deg) translate(5px, -6px);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Responsive
|
||||
======================================== */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.nav-toggle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-bg-primary);
|
||||
border-bottom: var(--border-width) solid var(--color-border);
|
||||
padding: var(--space-4) var(--container-padding);
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: transform var(--duration-normal) var(--ease-out),
|
||||
opacity var(--duration-normal) var(--ease-out),
|
||||
visibility var(--duration-normal);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.main-nav.is-open {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.nav-list {
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: block;
|
||||
padding: var(--space-3) var(--space-4);
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
.site-tagline {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
191
themes/swissfini/assets/css/main.css
Normal file
191
themes/swissfini/assets/css/main.css
Normal file
@@ -0,0 +1,191 @@
|
||||
/* ============================================
|
||||
SwissFini.sh Main Stylesheet
|
||||
"Precision Satire" Design System
|
||||
============================================ */
|
||||
|
||||
/*
|
||||
Build Order:
|
||||
1. Variables (design tokens)
|
||||
2. Reset (normalize defaults)
|
||||
3. Typography (base text styles)
|
||||
4. Themes (color variants)
|
||||
5. Components (UI elements)
|
||||
6. Utilities (helper classes)
|
||||
*/
|
||||
|
||||
/* ========================================
|
||||
Base Layer
|
||||
======================================== */
|
||||
|
||||
@import "base/_variables.css";
|
||||
@import "base/_reset.css";
|
||||
@import "base/_typography.css";
|
||||
|
||||
/* ========================================
|
||||
Theme Variants
|
||||
======================================== */
|
||||
|
||||
@import "themes/_dark.css";
|
||||
@import "themes/_high-contrast.css";
|
||||
|
||||
/* ========================================
|
||||
Components
|
||||
======================================== */
|
||||
|
||||
@import "components/_header.css";
|
||||
@import "components/_footer.css";
|
||||
@import "components/_article.css";
|
||||
@import "components/_accessibility-panel.css";
|
||||
|
||||
/* ========================================
|
||||
Utilities
|
||||
======================================== */
|
||||
|
||||
@import "utilities/_focus.css";
|
||||
@import "utilities/_print.css";
|
||||
|
||||
/* ========================================
|
||||
Layout Helpers
|
||||
======================================== */
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: var(--container-max);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: var(--container-padding);
|
||||
padding-right: var(--container-padding);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Page wrapper for sticky footer */
|
||||
.page-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Skip Links (Accessibility)
|
||||
======================================== */
|
||||
|
||||
.skip-links {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: var(--z-skip-link);
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -100%;
|
||||
left: var(--space-4);
|
||||
padding: var(--space-3) var(--space-6);
|
||||
background-color: var(--color-accent-navy);
|
||||
color: var(--color-text-inverse);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
text-decoration: none;
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: var(--shadow-lg);
|
||||
transition: top var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: var(--space-4);
|
||||
outline: 3px solid var(--color-accent-gold);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Font Size Scale (Accessibility Control)
|
||||
======================================== */
|
||||
|
||||
[data-font-size="1"] { --font-size-scale: 0.75; }
|
||||
[data-font-size="2"] { --font-size-scale: 0.875; }
|
||||
[data-font-size="3"] { --font-size-scale: 1; }
|
||||
[data-font-size="4"] { --font-size-scale: 1.25; }
|
||||
[data-font-size="5"] { --font-size-scale: 1.5; }
|
||||
|
||||
/* ========================================
|
||||
Motion Preferences
|
||||
======================================== */
|
||||
|
||||
/* Respect system preference */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* User toggle override */
|
||||
[data-reduced-motion="true"] *,
|
||||
[data-reduced-motion="true"] *::before,
|
||||
[data-reduced-motion="true"] *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Utility Classes
|
||||
======================================== */
|
||||
|
||||
/* Text alignment */
|
||||
.text-center { text-align: center; }
|
||||
.text-left { text-align: left; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
/* Display */
|
||||
.hidden { display: none !important; }
|
||||
.block { display: block; }
|
||||
.inline-block { display: inline-block; }
|
||||
.flex { display: flex; }
|
||||
.inline-flex { display: inline-flex; }
|
||||
.grid { display: grid; }
|
||||
|
||||
/* Flexbox */
|
||||
.items-center { align-items: center; }
|
||||
.justify-center { justify-content: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.flex-wrap { flex-wrap: wrap; }
|
||||
.gap-2 { gap: var(--space-2); }
|
||||
.gap-4 { gap: var(--space-4); }
|
||||
.gap-6 { gap: var(--space-6); }
|
||||
|
||||
/* Margin */
|
||||
.mt-4 { margin-top: var(--space-4); }
|
||||
.mt-8 { margin-top: var(--space-8); }
|
||||
.mb-4 { margin-bottom: var(--space-4); }
|
||||
.mb-8 { margin-bottom: var(--space-8); }
|
||||
.mx-auto { margin-left: auto; margin-right: auto; }
|
||||
|
||||
/* Padding */
|
||||
.p-4 { padding: var(--space-4); }
|
||||
.p-6 { padding: var(--space-6); }
|
||||
.p-8 { padding: var(--space-8); }
|
||||
.py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); }
|
||||
.py-8 { padding-top: var(--space-8); padding-bottom: var(--space-8); }
|
||||
.px-4 { padding-left: var(--space-4); padding-right: var(--space-4); }
|
||||
.px-6 { padding-left: var(--space-6); padding-right: var(--space-6); }
|
||||
|
||||
/* Width */
|
||||
.w-full { width: 100%; }
|
||||
.max-w-prose { max-width: var(--reading-width); }
|
||||
|
||||
/* Colors */
|
||||
.text-cardinal { color: var(--color-accent-cardinal); }
|
||||
.text-navy { color: var(--color-accent-navy); }
|
||||
.text-muted { color: var(--color-text-muted); }
|
||||
.bg-elevated { background-color: var(--color-bg-elevated); }
|
||||
116
themes/swissfini/assets/css/themes/_dark.css
Normal file
116
themes/swissfini/assets/css/themes/_dark.css
Normal file
@@ -0,0 +1,116 @@
|
||||
/* ============================================
|
||||
Dark Theme
|
||||
"Classified Documents" - Reading in the shadows
|
||||
============================================ */
|
||||
|
||||
[data-theme="dark"] {
|
||||
/* Paper & Background - Deep charcoal */
|
||||
--color-bg-primary: #0d0d0f;
|
||||
--color-bg-secondary: #151518;
|
||||
--color-bg-tertiary: #1c1c20;
|
||||
--color-bg-elevated: #222226;
|
||||
|
||||
/* Text - Soft whites for reduced eye strain */
|
||||
--color-text-primary: #e8e6e3;
|
||||
--color-text-secondary: #a8a5a0;
|
||||
--color-text-tertiary: #7a7772;
|
||||
--color-text-muted: #5a5855;
|
||||
--color-text-inverse: #0d0d0f;
|
||||
|
||||
/* Accents - Brightened for dark mode */
|
||||
--color-accent-cardinal: #e84a64;
|
||||
--color-accent-cardinal-hover: #ff5d78;
|
||||
--color-accent-cardinal-subtle: rgba(232, 74, 100, 0.15);
|
||||
|
||||
--color-accent-navy: #5a9fd4;
|
||||
--color-accent-navy-hover: #7ab4e0;
|
||||
--color-accent-navy-subtle: rgba(90, 159, 212, 0.12);
|
||||
|
||||
--color-accent-gold: #e6c84a;
|
||||
--color-accent-gold-subtle: rgba(230, 200, 74, 0.12);
|
||||
|
||||
/* Links */
|
||||
--color-link: #5a9fd4;
|
||||
--color-link-hover: #e84a64;
|
||||
--color-link-visited: #b088c0;
|
||||
|
||||
/* Semantic */
|
||||
--color-success: #4ade80;
|
||||
--color-warning: #fbbf24;
|
||||
--color-error: #e84a64;
|
||||
|
||||
/* Focus */
|
||||
--color-focus: #60a5fa;
|
||||
--color-focus-ring: rgba(96, 165, 250, 0.3);
|
||||
--color-focus-visible: #93c5fd;
|
||||
|
||||
/* Borders */
|
||||
--color-border: #2a2a2f;
|
||||
--color-border-strong: #3a3a40;
|
||||
--color-divider: #252528;
|
||||
|
||||
/* Shadows - Deeper for dark mode */
|
||||
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.35), 0 4px 6px -2px rgba(0, 0, 0, 0.2);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2);
|
||||
|
||||
--shadow-elevated:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.05),
|
||||
0 2px 4px rgba(0, 0, 0, 0.3),
|
||||
0 8px 16px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
/* Dark mode specific adjustments */
|
||||
[data-theme="dark"] code {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] pre {
|
||||
background-color: #0a0a0c;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
[data-theme="dark"] pre code {
|
||||
color: #e8e6e3;
|
||||
}
|
||||
|
||||
[data-theme="dark"] blockquote {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(230, 200, 74, 0.08) 0%,
|
||||
transparent 50%
|
||||
);
|
||||
}
|
||||
|
||||
[data-theme="dark"] hr.ornament::after {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
var(--color-border) 15%,
|
||||
transparent 45%,
|
||||
transparent 55%,
|
||||
var(--color-border) 85%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
[data-theme="dark"] ::selection {
|
||||
background-color: rgba(232, 74, 100, 0.3);
|
||||
}
|
||||
|
||||
/* Subtle vignette effect for reading focus */
|
||||
[data-theme="dark"] body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
transparent 0%,
|
||||
transparent 60%,
|
||||
rgba(0, 0, 0, 0.15) 100%
|
||||
);
|
||||
z-index: -1;
|
||||
}
|
||||
141
themes/swissfini/assets/css/themes/_high-contrast.css
Normal file
141
themes/swissfini/assets/css/themes/_high-contrast.css
Normal file
@@ -0,0 +1,141 @@
|
||||
/* ============================================
|
||||
High Contrast Theme
|
||||
Maximum accessibility - WCAG AAA compliant
|
||||
============================================ */
|
||||
|
||||
[data-theme="high-contrast"] {
|
||||
/* Pure black & white base */
|
||||
--color-bg-primary: #000000;
|
||||
--color-bg-secondary: #0a0a0a;
|
||||
--color-bg-tertiary: #141414;
|
||||
--color-bg-elevated: #1a1a1a;
|
||||
|
||||
/* Pure white text */
|
||||
--color-text-primary: #ffffff;
|
||||
--color-text-secondary: #ffffff;
|
||||
--color-text-tertiary: #e0e0e0;
|
||||
--color-text-muted: #c0c0c0;
|
||||
--color-text-inverse: #000000;
|
||||
|
||||
/* High visibility accents */
|
||||
--color-accent-cardinal: #ff4d6d;
|
||||
--color-accent-cardinal-hover: #ff7a93;
|
||||
--color-accent-cardinal-subtle: rgba(255, 77, 109, 0.2);
|
||||
|
||||
--color-accent-navy: #00e5ff;
|
||||
--color-accent-navy-hover: #4df0ff;
|
||||
--color-accent-navy-subtle: rgba(0, 229, 255, 0.15);
|
||||
|
||||
--color-accent-gold: #ffff00;
|
||||
--color-accent-gold-subtle: rgba(255, 255, 0, 0.15);
|
||||
|
||||
/* Links - Bright cyan for visibility */
|
||||
--color-link: #00e5ff;
|
||||
--color-link-hover: #ffff00;
|
||||
--color-link-visited: #ff80ff;
|
||||
|
||||
/* Semantic - High visibility */
|
||||
--color-success: #00ff7f;
|
||||
--color-warning: #ffff00;
|
||||
--color-error: #ff4d6d;
|
||||
|
||||
/* Focus - Bright yellow for maximum visibility */
|
||||
--color-focus: #ffff00;
|
||||
--color-focus-ring: rgba(255, 255, 0, 0.4);
|
||||
--color-focus-visible: #ffff00;
|
||||
|
||||
/* Borders - High contrast */
|
||||
--color-border: #ffffff;
|
||||
--color-border-strong: #ffffff;
|
||||
--color-divider: #444444;
|
||||
|
||||
/* Stronger borders */
|
||||
--border-width: 2px;
|
||||
--border-width-thick: 3px;
|
||||
--border-width-heavy: 5px;
|
||||
|
||||
/* Focus indicators - Extra visible */
|
||||
--focus-outline-width: 4px;
|
||||
--focus-outline-offset: 4px;
|
||||
}
|
||||
|
||||
/* High contrast specific overrides */
|
||||
[data-theme="high-contrast"] a {
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] a:hover {
|
||||
text-decoration-thickness: 3px;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] h2 {
|
||||
border-bottom-color: var(--color-accent-cardinal);
|
||||
border-bottom-width: 3px;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] blockquote {
|
||||
background: rgba(255, 255, 0, 0.1);
|
||||
border-left-color: var(--color-accent-gold);
|
||||
border-left-width: 5px;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] blockquote::before {
|
||||
color: var(--color-accent-gold);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] code {
|
||||
background-color: #1a1a1a;
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] pre {
|
||||
background-color: #0a0a0a;
|
||||
border: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] hr {
|
||||
background: var(--color-border);
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] ::selection {
|
||||
background-color: var(--color-accent-gold);
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* All interactive elements get high contrast treatment */
|
||||
[data-theme="high-contrast"] button,
|
||||
[data-theme="high-contrast"] [role="button"],
|
||||
[data-theme="high-contrast"] input,
|
||||
[data-theme="high-contrast"] select,
|
||||
[data-theme="high-contrast"] textarea {
|
||||
border: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] button:hover,
|
||||
[data-theme="high-contrast"] [role="button"]:hover {
|
||||
background-color: var(--color-accent-gold);
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Table enhancements */
|
||||
[data-theme="high-contrast"] th {
|
||||
background-color: var(--color-accent-navy);
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] td {
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
/* Drop cap high contrast */
|
||||
[data-theme="high-contrast"] .drop-cap::first-letter,
|
||||
[data-theme="high-contrast"] .article-body > p:first-of-type::first-letter {
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
/* Remove subtle gradients that might reduce contrast */
|
||||
[data-theme="high-contrast"] blockquote {
|
||||
background: rgba(255, 255, 0, 0.1);
|
||||
}
|
||||
123
themes/swissfini/assets/css/utilities/_focus.css
Normal file
123
themes/swissfini/assets/css/utilities/_focus.css
Normal file
@@ -0,0 +1,123 @@
|
||||
/* ============================================
|
||||
Focus Indicators - WCAG 2.2 Compliant
|
||||
Visible, consistent, customizable
|
||||
============================================ */
|
||||
|
||||
/* ========================================
|
||||
Default Focus Styles
|
||||
======================================== */
|
||||
|
||||
/* Remove default outline, we'll add our own */
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Focus visible for keyboard navigation */
|
||||
:focus-visible {
|
||||
outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
|
||||
outline-offset: var(--focus-outline-offset);
|
||||
}
|
||||
|
||||
/* Skip focus ring for mouse clicks */
|
||||
:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Enhanced Focus Mode
|
||||
======================================== */
|
||||
|
||||
[data-enhanced-focus="true"] :focus-visible {
|
||||
outline-width: var(--focus-enhanced-width);
|
||||
outline-offset: var(--focus-enhanced-offset);
|
||||
box-shadow: 0 0 0 calc(var(--focus-enhanced-width) + 3px) var(--color-focus-ring);
|
||||
}
|
||||
|
||||
/* Extra visible for interactive elements */
|
||||
[data-enhanced-focus="true"] button:focus-visible,
|
||||
[data-enhanced-focus="true"] a:focus-visible,
|
||||
[data-enhanced-focus="true"] input:focus-visible,
|
||||
[data-enhanced-focus="true"] select:focus-visible,
|
||||
[data-enhanced-focus="true"] textarea:focus-visible,
|
||||
[data-enhanced-focus="true"] [role="button"]:focus-visible,
|
||||
[data-enhanced-focus="true"] [tabindex]:focus-visible {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Component-Specific Focus Styles
|
||||
======================================== */
|
||||
|
||||
/* Buttons */
|
||||
button:focus-visible,
|
||||
.btn:focus-visible,
|
||||
[role="button"]:focus-visible {
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a:focus-visible {
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
/* Form inputs */
|
||||
input:focus-visible,
|
||||
select:focus-visible,
|
||||
textarea:focus-visible {
|
||||
outline-offset: 0;
|
||||
box-shadow: 0 0 0 3px var(--color-focus-ring);
|
||||
}
|
||||
|
||||
/* Cards and containers */
|
||||
article:focus-visible,
|
||||
[role="article"]:focus-visible,
|
||||
.card:focus-visible {
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Skip Link Focus
|
||||
======================================== */
|
||||
|
||||
.skip-link:focus {
|
||||
position: fixed;
|
||||
top: var(--space-4);
|
||||
left: var(--space-4);
|
||||
z-index: var(--z-skip-link);
|
||||
padding: var(--space-3) var(--space-6);
|
||||
background-color: var(--color-accent-navy);
|
||||
color: var(--color-text-inverse);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
text-decoration: none;
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: var(--shadow-lg);
|
||||
outline: 3px solid var(--color-accent-gold);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Focus Within (for composite widgets)
|
||||
======================================== */
|
||||
|
||||
[data-enhanced-focus="true"] fieldset:focus-within {
|
||||
outline: 2px dashed var(--color-focus);
|
||||
outline-offset: 4px;
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
High Contrast Mode Focus
|
||||
======================================== */
|
||||
|
||||
[data-theme="high-contrast"] :focus-visible {
|
||||
outline-color: var(--color-focus);
|
||||
outline-width: 4px;
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"][data-enhanced-focus="true"] :focus-visible {
|
||||
outline-width: 5px;
|
||||
box-shadow: 0 0 0 8px var(--color-focus-ring);
|
||||
}
|
||||
380
themes/swissfini/assets/css/utilities/_print.css
Normal file
380
themes/swissfini/assets/css/utilities/_print.css
Normal file
@@ -0,0 +1,380 @@
|
||||
/* ============================================
|
||||
Print Styles
|
||||
Clean, readable printed output
|
||||
============================================ */
|
||||
|
||||
@media print {
|
||||
/* ========================================
|
||||
Reset for Print
|
||||
======================================== */
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
background: transparent !important;
|
||||
color: #000 !important;
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Page Setup
|
||||
======================================== */
|
||||
|
||||
@page {
|
||||
margin: 2cm;
|
||||
size: A4;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Georgia, 'Times New Roman', Times, serif;
|
||||
line-height: 1.5;
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Typography for Print
|
||||
======================================== */
|
||||
|
||||
h1 {
|
||||
font-size: 24pt;
|
||||
margin-bottom: 12pt;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18pt;
|
||||
margin-top: 24pt;
|
||||
margin-bottom: 12pt;
|
||||
border-bottom: 1pt solid #000;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 14pt;
|
||||
margin-top: 18pt;
|
||||
margin-bottom: 8pt;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
h4, h5, h6 {
|
||||
font-size: 12pt;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 11pt;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 10pt;
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}
|
||||
|
||||
/* Drop cap for first paragraph */
|
||||
.article-body > p:first-of-type::first-letter {
|
||||
font-size: 36pt;
|
||||
float: left;
|
||||
line-height: 1;
|
||||
margin-right: 6pt;
|
||||
margin-top: 2pt;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Links
|
||||
======================================== */
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Show URLs after external links */
|
||||
a[href^="http"]::after,
|
||||
a[href^="https"]::after {
|
||||
content: " (" attr(href) ")";
|
||||
font-size: 9pt;
|
||||
font-style: italic;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* Don't show URL for internal links */
|
||||
a[href^="/"]::after,
|
||||
a[href^="#"]::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Hide Non-Essential Elements
|
||||
======================================== */
|
||||
|
||||
.site-header,
|
||||
.site-footer,
|
||||
.main-nav,
|
||||
.accessibility-toggle,
|
||||
.accessibility-panel,
|
||||
.accessibility-backdrop,
|
||||
.skip-links,
|
||||
.nav-toggle,
|
||||
.cta-button,
|
||||
.paywall-box,
|
||||
.fade-overlay,
|
||||
.header-actions,
|
||||
nav,
|
||||
aside,
|
||||
[role="complementary"],
|
||||
[aria-hidden="true"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Article Styling
|
||||
======================================== */
|
||||
|
||||
.article {
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.article-header {
|
||||
border-bottom: 1pt solid #000;
|
||||
padding-bottom: 12pt;
|
||||
margin-bottom: 18pt;
|
||||
}
|
||||
|
||||
.article-category {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1pt;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
font-size: 28pt;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.article-subtitle {
|
||||
font-size: 14pt;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
font-size: 10pt;
|
||||
border-top: 0.5pt solid #666;
|
||||
padding-top: 8pt;
|
||||
margin-top: 12pt;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Tables
|
||||
======================================== */
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 12pt 0;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #ddd !important;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding: 6pt 8pt;
|
||||
border: 0.5pt solid #000;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 6pt 8pt;
|
||||
border: 0.5pt solid #000;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f5f5f5 !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Blockquotes
|
||||
======================================== */
|
||||
|
||||
blockquote {
|
||||
margin: 12pt 0;
|
||||
padding: 8pt 12pt;
|
||||
border-left: 3pt solid #000;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
blockquote::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
blockquote cite {
|
||||
display: block;
|
||||
margin-top: 6pt;
|
||||
font-size: 10pt;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Irony Box
|
||||
======================================== */
|
||||
|
||||
.irony-box {
|
||||
margin: 12pt 0;
|
||||
padding: 10pt;
|
||||
border: 2pt solid #000;
|
||||
background-color: #f9f9f9 !important;
|
||||
}
|
||||
|
||||
.irony-box::before {
|
||||
content: "Note: ";
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Conclusion
|
||||
======================================== */
|
||||
|
||||
.conclusion {
|
||||
margin: 18pt 0;
|
||||
padding: 12pt;
|
||||
border: 1pt solid #000;
|
||||
background-color: #eee !important;
|
||||
}
|
||||
|
||||
.conclusion h3 {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Sources
|
||||
======================================== */
|
||||
|
||||
.sources {
|
||||
margin-top: 24pt;
|
||||
padding-top: 12pt;
|
||||
border-top: 1pt solid #000;
|
||||
}
|
||||
|
||||
.sources h3 {
|
||||
font-size: 12pt;
|
||||
margin-bottom: 8pt;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.sources ul {
|
||||
font-size: 9pt;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.sources a::after {
|
||||
content: " [" attr(href) "]";
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Code
|
||||
======================================== */
|
||||
|
||||
code {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 10pt;
|
||||
border: 0.5pt solid #ccc;
|
||||
padding: 1pt 3pt;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 9pt;
|
||||
border: 0.5pt solid #000;
|
||||
padding: 8pt;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
pre code {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Page Breaks
|
||||
======================================== */
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
p, blockquote, table, ul, ol {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
img, figure {
|
||||
page-break-inside: avoid;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Images
|
||||
======================================== */
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 12pt 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
font-size: 10pt;
|
||||
font-style: italic;
|
||||
margin-top: 6pt;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Lists
|
||||
======================================== */
|
||||
|
||||
ul, ol {
|
||||
margin: 10pt 0;
|
||||
padding-left: 20pt;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 11pt;
|
||||
margin-bottom: 4pt;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
Print Header/Footer
|
||||
======================================== */
|
||||
|
||||
.print-header {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 10pt;
|
||||
margin-bottom: 18pt;
|
||||
padding-bottom: 12pt;
|
||||
border-bottom: 0.5pt solid #000;
|
||||
}
|
||||
|
||||
.print-footer {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 9pt;
|
||||
margin-top: 24pt;
|
||||
padding-top: 12pt;
|
||||
border-top: 0.5pt solid #000;
|
||||
}
|
||||
}
|
||||
660
themes/swissfini/assets/js/accessibility.js
Normal file
660
themes/swissfini/assets/js/accessibility.js
Normal file
@@ -0,0 +1,660 @@
|
||||
/**
|
||||
* SwissFini.sh Accessibility Controller
|
||||
* WCAG 2.2 compliant self-service accessibility controls
|
||||
*
|
||||
* Features:
|
||||
* - Font size adjustment (5 levels: 75%-150%)
|
||||
* - Theme toggle (light/dark/high-contrast/system)
|
||||
* - Dyslexia-friendly font toggle
|
||||
* - Line spacing control (4 levels)
|
||||
* - Reduced motion toggle
|
||||
* - Reading width control (3 levels)
|
||||
* - Enhanced focus indicators toggle
|
||||
* - All preferences persisted in localStorage
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// ============================================
|
||||
// Configuration
|
||||
// ============================================
|
||||
|
||||
const STORAGE_KEY = 'swissfini_accessibility';
|
||||
const FIRST_VISIT_KEY = 'swissfini_first_visit';
|
||||
|
||||
const DEFAULTS = {
|
||||
fontSize: 3, // 1-5 scale, 3 = 100%
|
||||
theme: 'system', // 'light', 'dark', 'high-contrast', 'system'
|
||||
dyslexiaFont: false,
|
||||
lineSpacing: 'normal', // 'compact', 'normal', 'relaxed', 'loose'
|
||||
reducedMotion: 'system', // 'true', 'false', 'system'
|
||||
readingWidth: 'medium', // 'narrow', 'medium', 'wide'
|
||||
enhancedFocus: false
|
||||
};
|
||||
|
||||
const FONT_SIZES = {
|
||||
1: 0.75, // 75%
|
||||
2: 0.875, // 87.5%
|
||||
3: 1, // 100%
|
||||
4: 1.25, // 125%
|
||||
5: 1.5 // 150%
|
||||
};
|
||||
|
||||
const FONT_SIZE_LABELS = {
|
||||
1: '75%',
|
||||
2: '88%',
|
||||
3: '100%',
|
||||
4: '125%',
|
||||
5: '150%'
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// State Management
|
||||
// ============================================
|
||||
|
||||
let state = { ...DEFAULTS };
|
||||
|
||||
function loadPreferences() {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored);
|
||||
state = { ...DEFAULTS, ...parsed };
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Could not load accessibility preferences:', e);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function savePreferences() {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
||||
} catch (e) {
|
||||
console.warn('Could not save accessibility preferences:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function isFirstVisit() {
|
||||
try {
|
||||
return !localStorage.getItem(FIRST_VISIT_KEY);
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function markVisited() {
|
||||
try {
|
||||
localStorage.setItem(FIRST_VISIT_KEY, 'true');
|
||||
} catch (e) {
|
||||
// Silent fail
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// DOM Manipulation
|
||||
// ============================================
|
||||
|
||||
const html = document.documentElement;
|
||||
|
||||
function applyFontSize(level) {
|
||||
html.setAttribute('data-font-size', level);
|
||||
|
||||
// Update label if exists
|
||||
const label = document.getElementById('font-size-label');
|
||||
if (label) {
|
||||
label.textContent = FONT_SIZE_LABELS[level] || '100%';
|
||||
}
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
// Remove existing theme
|
||||
html.removeAttribute('data-theme');
|
||||
|
||||
let effectiveTheme = theme;
|
||||
|
||||
if (theme === 'system') {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
effectiveTheme = prefersDark ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
if (effectiveTheme !== 'light') {
|
||||
html.setAttribute('data-theme', effectiveTheme);
|
||||
}
|
||||
|
||||
// Update meta theme-color
|
||||
const metaTheme = document.querySelector('meta[name="theme-color"]');
|
||||
if (metaTheme) {
|
||||
const colors = {
|
||||
light: '#faf9f7',
|
||||
dark: '#0d0d0f',
|
||||
'high-contrast': '#000000'
|
||||
};
|
||||
metaTheme.setAttribute('content', colors[effectiveTheme] || colors.light);
|
||||
}
|
||||
}
|
||||
|
||||
function applyDyslexiaFont(enabled) {
|
||||
html.setAttribute('data-dyslexia', enabled.toString());
|
||||
}
|
||||
|
||||
function applyLineSpacing(spacing) {
|
||||
html.setAttribute('data-line-spacing', spacing);
|
||||
}
|
||||
|
||||
function applyReducedMotion(preference) {
|
||||
if (preference === 'system') {
|
||||
html.removeAttribute('data-reduced-motion');
|
||||
} else {
|
||||
html.setAttribute('data-reduced-motion', preference);
|
||||
}
|
||||
}
|
||||
|
||||
function applyReadingWidth(width) {
|
||||
html.setAttribute('data-reading-width', width);
|
||||
}
|
||||
|
||||
function applyEnhancedFocus(enabled) {
|
||||
html.setAttribute('data-enhanced-focus', enabled.toString());
|
||||
}
|
||||
|
||||
function applyAllPreferences() {
|
||||
applyFontSize(state.fontSize);
|
||||
applyTheme(state.theme);
|
||||
applyDyslexiaFont(state.dyslexiaFont);
|
||||
applyLineSpacing(state.lineSpacing);
|
||||
applyReducedMotion(state.reducedMotion);
|
||||
applyReadingWidth(state.readingWidth);
|
||||
applyEnhancedFocus(state.enhancedFocus);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Event Handlers
|
||||
// ============================================
|
||||
|
||||
function handleFontSizeChange(direction) {
|
||||
const newSize = Math.max(1, Math.min(5, state.fontSize + direction));
|
||||
if (newSize !== state.fontSize) {
|
||||
state.fontSize = newSize;
|
||||
applyFontSize(newSize);
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
announceChange(`Font size changed to ${FONT_SIZE_LABELS[newSize]}`);
|
||||
}
|
||||
}
|
||||
|
||||
function handleThemeChange(theme) {
|
||||
state.theme = theme;
|
||||
applyTheme(theme);
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
|
||||
const themeNames = {
|
||||
light: 'light mode',
|
||||
dark: 'dark mode',
|
||||
'high-contrast': 'high contrast mode',
|
||||
system: 'system preference'
|
||||
};
|
||||
announceChange(`Theme changed to ${themeNames[theme]}`);
|
||||
}
|
||||
|
||||
function handleDyslexiaToggle() {
|
||||
state.dyslexiaFont = !state.dyslexiaFont;
|
||||
applyDyslexiaFont(state.dyslexiaFont);
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
announceChange(`Dyslexia-friendly font ${state.dyslexiaFont ? 'enabled' : 'disabled'}`);
|
||||
}
|
||||
|
||||
function handleLineSpacingChange(spacing) {
|
||||
state.lineSpacing = spacing;
|
||||
applyLineSpacing(spacing);
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
announceChange(`Line spacing set to ${spacing}`);
|
||||
}
|
||||
|
||||
function handleReducedMotionChange(preference) {
|
||||
state.reducedMotion = preference;
|
||||
applyReducedMotion(preference);
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
|
||||
const labels = {
|
||||
true: 'enabled',
|
||||
false: 'disabled',
|
||||
system: 'set to system preference'
|
||||
};
|
||||
announceChange(`Reduced motion ${labels[preference]}`);
|
||||
}
|
||||
|
||||
function handleReadingWidthChange(width) {
|
||||
state.readingWidth = width;
|
||||
applyReadingWidth(width);
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
announceChange(`Reading width set to ${width}`);
|
||||
}
|
||||
|
||||
function handleEnhancedFocusToggle() {
|
||||
state.enhancedFocus = !state.enhancedFocus;
|
||||
applyEnhancedFocus(state.enhancedFocus);
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
announceChange(`Enhanced focus indicators ${state.enhancedFocus ? 'enabled' : 'disabled'}`);
|
||||
}
|
||||
|
||||
function handleResetPreferences() {
|
||||
state = { ...DEFAULTS };
|
||||
applyAllPreferences();
|
||||
savePreferences();
|
||||
updateControlStates();
|
||||
announceChange('All accessibility preferences reset to defaults');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Screen Reader Announcements
|
||||
// ============================================
|
||||
|
||||
let announcer = null;
|
||||
|
||||
function createAnnouncer() {
|
||||
announcer = document.createElement('div');
|
||||
announcer.id = 'accessibility-announcer';
|
||||
announcer.setAttribute('role', 'status');
|
||||
announcer.setAttribute('aria-live', 'polite');
|
||||
announcer.setAttribute('aria-atomic', 'true');
|
||||
announcer.className = 'accessibility-announcer';
|
||||
document.body.appendChild(announcer);
|
||||
}
|
||||
|
||||
function announceChange(message) {
|
||||
if (!announcer) createAnnouncer();
|
||||
|
||||
// Clear and re-announce for screen readers
|
||||
announcer.textContent = '';
|
||||
requestAnimationFrame(() => {
|
||||
announcer.textContent = message;
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Panel Toggle & Focus Management
|
||||
// ============================================
|
||||
|
||||
let lastFocusedElement = null;
|
||||
|
||||
function initPanelToggle() {
|
||||
const toggleBtn = document.getElementById('accessibility-toggle');
|
||||
const panel = document.getElementById('accessibility-panel');
|
||||
const closeBtn = document.getElementById('accessibility-close');
|
||||
const backdrop = document.getElementById('accessibility-backdrop');
|
||||
|
||||
if (!toggleBtn || !panel) return;
|
||||
|
||||
function openPanel() {
|
||||
lastFocusedElement = document.activeElement;
|
||||
|
||||
panel.classList.add('is-open');
|
||||
panel.setAttribute('aria-hidden', 'false');
|
||||
toggleBtn.setAttribute('aria-expanded', 'true');
|
||||
|
||||
if (backdrop) {
|
||||
backdrop.classList.add('is-visible');
|
||||
}
|
||||
|
||||
// Focus first focusable element
|
||||
const firstFocusable = panel.querySelector(
|
||||
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
||||
);
|
||||
if (firstFocusable) {
|
||||
setTimeout(() => firstFocusable.focus(), 50);
|
||||
}
|
||||
|
||||
// Add event listeners
|
||||
document.addEventListener('keydown', handlePanelKeydown);
|
||||
}
|
||||
|
||||
function closePanel() {
|
||||
panel.classList.remove('is-open');
|
||||
panel.setAttribute('aria-hidden', 'true');
|
||||
toggleBtn.setAttribute('aria-expanded', 'false');
|
||||
|
||||
if (backdrop) {
|
||||
backdrop.classList.remove('is-visible');
|
||||
}
|
||||
|
||||
// Restore focus
|
||||
if (lastFocusedElement) {
|
||||
lastFocusedElement.focus();
|
||||
}
|
||||
|
||||
// Remove event listeners
|
||||
document.removeEventListener('keydown', handlePanelKeydown);
|
||||
}
|
||||
|
||||
function handlePanelKeydown(e) {
|
||||
// Close on Escape
|
||||
if (e.key === 'Escape') {
|
||||
closePanel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Focus trapping
|
||||
if (e.key === 'Tab') {
|
||||
const focusables = panel.querySelectorAll(
|
||||
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
||||
);
|
||||
|
||||
if (focusables.length === 0) return;
|
||||
|
||||
const firstFocusable = focusables[0];
|
||||
const lastFocusable = focusables[focusables.length - 1];
|
||||
|
||||
if (e.shiftKey && document.activeElement === firstFocusable) {
|
||||
e.preventDefault();
|
||||
lastFocusable.focus();
|
||||
} else if (!e.shiftKey && document.activeElement === lastFocusable) {
|
||||
e.preventDefault();
|
||||
firstFocusable.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle button click
|
||||
toggleBtn.addEventListener('click', () => {
|
||||
const isOpen = panel.classList.contains('is-open');
|
||||
if (isOpen) {
|
||||
closePanel();
|
||||
} else {
|
||||
openPanel();
|
||||
}
|
||||
});
|
||||
|
||||
// Close button click
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('click', closePanel);
|
||||
}
|
||||
|
||||
// Backdrop click
|
||||
if (backdrop) {
|
||||
backdrop.addEventListener('click', closePanel);
|
||||
}
|
||||
|
||||
// Remove first-visit animation after interaction
|
||||
toggleBtn.addEventListener('click', () => {
|
||||
toggleBtn.classList.remove('is-new');
|
||||
markVisited();
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Control State Synchronization
|
||||
// ============================================
|
||||
|
||||
function updateControlStates() {
|
||||
// Font size buttons
|
||||
const decreaseBtn = document.getElementById('font-size-decrease');
|
||||
const increaseBtn = document.getElementById('font-size-increase');
|
||||
const fontLabel = document.getElementById('font-size-label');
|
||||
|
||||
if (decreaseBtn) decreaseBtn.disabled = state.fontSize <= 1;
|
||||
if (increaseBtn) increaseBtn.disabled = state.fontSize >= 5;
|
||||
if (fontLabel) fontLabel.textContent = FONT_SIZE_LABELS[state.fontSize];
|
||||
|
||||
// Theme buttons
|
||||
document.querySelectorAll('[data-theme-option]').forEach(btn => {
|
||||
const isActive = btn.dataset.themeOption === state.theme;
|
||||
btn.setAttribute('aria-pressed', isActive);
|
||||
});
|
||||
|
||||
// Dyslexia toggle
|
||||
const dyslexiaBtn = document.getElementById('dyslexia-toggle');
|
||||
if (dyslexiaBtn) {
|
||||
dyslexiaBtn.setAttribute('aria-pressed', state.dyslexiaFont);
|
||||
}
|
||||
|
||||
// Line spacing buttons
|
||||
document.querySelectorAll('[data-spacing-option]').forEach(btn => {
|
||||
const isActive = btn.dataset.spacingOption === state.lineSpacing;
|
||||
btn.setAttribute('aria-pressed', isActive);
|
||||
});
|
||||
|
||||
// Reduced motion buttons
|
||||
document.querySelectorAll('[data-motion-option]').forEach(btn => {
|
||||
const isActive = btn.dataset.motionOption === state.reducedMotion;
|
||||
btn.setAttribute('aria-pressed', isActive);
|
||||
});
|
||||
|
||||
// Reading width buttons
|
||||
document.querySelectorAll('[data-width-option]').forEach(btn => {
|
||||
const isActive = btn.dataset.widthOption === state.readingWidth;
|
||||
btn.setAttribute('aria-pressed', isActive);
|
||||
});
|
||||
|
||||
// Enhanced focus toggle
|
||||
const focusBtn = document.getElementById('enhanced-focus-toggle');
|
||||
if (focusBtn) {
|
||||
focusBtn.setAttribute('aria-pressed', state.enhancedFocus);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Event Binding
|
||||
// ============================================
|
||||
|
||||
function bindEvents() {
|
||||
// Font size
|
||||
const decreaseBtn = document.getElementById('font-size-decrease');
|
||||
const increaseBtn = document.getElementById('font-size-increase');
|
||||
|
||||
if (decreaseBtn) {
|
||||
decreaseBtn.addEventListener('click', () => handleFontSizeChange(-1));
|
||||
}
|
||||
if (increaseBtn) {
|
||||
increaseBtn.addEventListener('click', () => handleFontSizeChange(1));
|
||||
}
|
||||
|
||||
// Theme
|
||||
document.querySelectorAll('[data-theme-option]').forEach(btn => {
|
||||
btn.addEventListener('click', () => handleThemeChange(btn.dataset.themeOption));
|
||||
});
|
||||
|
||||
// Dyslexia
|
||||
const dyslexiaBtn = document.getElementById('dyslexia-toggle');
|
||||
if (dyslexiaBtn) {
|
||||
dyslexiaBtn.addEventListener('click', handleDyslexiaToggle);
|
||||
}
|
||||
|
||||
// Line spacing
|
||||
document.querySelectorAll('[data-spacing-option]').forEach(btn => {
|
||||
btn.addEventListener('click', () => handleLineSpacingChange(btn.dataset.spacingOption));
|
||||
});
|
||||
|
||||
// Reduced motion
|
||||
document.querySelectorAll('[data-motion-option]').forEach(btn => {
|
||||
btn.addEventListener('click', () => handleReducedMotionChange(btn.dataset.motionOption));
|
||||
});
|
||||
|
||||
// Reading width
|
||||
document.querySelectorAll('[data-width-option]').forEach(btn => {
|
||||
btn.addEventListener('click', () => handleReadingWidthChange(btn.dataset.widthOption));
|
||||
});
|
||||
|
||||
// Enhanced focus
|
||||
const focusBtn = document.getElementById('enhanced-focus-toggle');
|
||||
if (focusBtn) {
|
||||
focusBtn.addEventListener('click', handleEnhancedFocusToggle);
|
||||
}
|
||||
|
||||
// Reset
|
||||
const resetBtn = document.getElementById('accessibility-reset');
|
||||
if (resetBtn) {
|
||||
resetBtn.addEventListener('click', handleResetPreferences);
|
||||
}
|
||||
|
||||
// System preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (state.theme === 'system') {
|
||||
applyTheme('system');
|
||||
}
|
||||
});
|
||||
|
||||
window.matchMedia('(prefers-reduced-motion: reduce)').addEventListener('change', () => {
|
||||
if (state.reducedMotion === 'system') {
|
||||
applyReducedMotion('system');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Header Scroll Effect
|
||||
// ============================================
|
||||
|
||||
function initHeaderScroll() {
|
||||
const header = document.querySelector('.site-header');
|
||||
if (!header) return;
|
||||
|
||||
let lastScroll = 0;
|
||||
const scrollThreshold = 10;
|
||||
|
||||
function handleScroll() {
|
||||
const currentScroll = window.scrollY;
|
||||
|
||||
if (currentScroll > scrollThreshold) {
|
||||
header.classList.add('scrolled');
|
||||
} else {
|
||||
header.classList.remove('scrolled');
|
||||
}
|
||||
|
||||
lastScroll = currentScroll;
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Mobile Navigation
|
||||
// ============================================
|
||||
|
||||
function initMobileNav() {
|
||||
const toggle = document.querySelector('.nav-toggle');
|
||||
const nav = document.querySelector('.main-nav');
|
||||
|
||||
if (!toggle || !nav) return;
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
const isOpen = nav.classList.contains('is-open');
|
||||
|
||||
nav.classList.toggle('is-open');
|
||||
toggle.setAttribute('aria-expanded', !isOpen);
|
||||
});
|
||||
|
||||
// Close on click outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!nav.contains(e.target) && !toggle.contains(e.target)) {
|
||||
nav.classList.remove('is-open');
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
});
|
||||
|
||||
// Close on escape
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && nav.classList.contains('is-open')) {
|
||||
nav.classList.remove('is-open');
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
toggle.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Initialization
|
||||
// ============================================
|
||||
|
||||
function init() {
|
||||
loadPreferences();
|
||||
applyAllPreferences();
|
||||
|
||||
// Wait for DOM
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', onDOMReady);
|
||||
} else {
|
||||
onDOMReady();
|
||||
}
|
||||
}
|
||||
|
||||
function onDOMReady() {
|
||||
bindEvents();
|
||||
initPanelToggle();
|
||||
initHeaderScroll();
|
||||
initMobileNav();
|
||||
updateControlStates();
|
||||
|
||||
// Show first-visit animation
|
||||
if (isFirstVisit()) {
|
||||
const toggleBtn = document.getElementById('accessibility-toggle');
|
||||
if (toggleBtn) {
|
||||
toggleBtn.classList.add('is-new');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Critical Styles (Prevent Flash)
|
||||
// ============================================
|
||||
|
||||
(function applyCriticalStyles() {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const prefs = JSON.parse(stored);
|
||||
|
||||
// Apply theme immediately
|
||||
if (prefs.theme && prefs.theme !== 'light') {
|
||||
if (prefs.theme === 'system') {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (prefersDark) {
|
||||
html.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
} else {
|
||||
html.setAttribute('data-theme', prefs.theme);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply font scale
|
||||
if (prefs.fontSize && FONT_SIZES[prefs.fontSize]) {
|
||||
html.setAttribute('data-font-size', prefs.fontSize);
|
||||
}
|
||||
|
||||
// Apply dyslexia font
|
||||
if (prefs.dyslexiaFont) {
|
||||
html.setAttribute('data-dyslexia', 'true');
|
||||
}
|
||||
|
||||
// Apply other critical preferences
|
||||
if (prefs.lineSpacing) {
|
||||
html.setAttribute('data-line-spacing', prefs.lineSpacing);
|
||||
}
|
||||
|
||||
if (prefs.readingWidth) {
|
||||
html.setAttribute('data-reading-width', prefs.readingWidth);
|
||||
}
|
||||
|
||||
if (prefs.enhancedFocus) {
|
||||
html.setAttribute('data-enhanced-focus', 'true');
|
||||
}
|
||||
|
||||
if (prefs.reducedMotion && prefs.reducedMotion !== 'system') {
|
||||
html.setAttribute('data-reduced-motion', prefs.reducedMotion);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail
|
||||
}
|
||||
})();
|
||||
|
||||
// Start
|
||||
init();
|
||||
})();
|
||||
27
themes/swissfini/layouts/_default/baseof.html
Normal file
27
themes/swissfini/layouts/_default/baseof.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.Language.Lang | default "en" }}">
|
||||
<head>
|
||||
{{- partial "head.html" . -}}
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-wrapper">
|
||||
{{- partial "skip-links.html" . -}}
|
||||
{{- partial "header.html" . -}}
|
||||
|
||||
<main id="main-content" class="main-content" role="main">
|
||||
{{- block "main" . }}{{- end }}
|
||||
</main>
|
||||
|
||||
{{- partial "footer.html" . -}}
|
||||
</div>
|
||||
|
||||
{{- partial "accessibility-panel.html" . -}}
|
||||
|
||||
{{/* Backdrop for panel */}}
|
||||
<div id="accessibility-backdrop" class="accessibility-backdrop" aria-hidden="true"></div>
|
||||
|
||||
{{/* Load JavaScript */}}
|
||||
{{ $js := resources.Get "js/accessibility.js" | minify | fingerprint }}
|
||||
<script src="{{ $js.RelPermalink }}" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
99
themes/swissfini/layouts/_default/list.html
Normal file
99
themes/swissfini/layouts/_default/list.html
Normal file
@@ -0,0 +1,99 @@
|
||||
{{ define "main" }}
|
||||
<div class="container py-8">
|
||||
<header class="mb-8">
|
||||
<h1 class="article-title">{{ .Title }}</h1>
|
||||
{{ with .Description }}
|
||||
<p class="lead">{{ . }}</p>
|
||||
{{ end }}
|
||||
</header>
|
||||
|
||||
{{ if .Pages }}
|
||||
<div class="article-list">
|
||||
{{ range .Pages }}
|
||||
<article class="article-card">
|
||||
<a href="{{ .Permalink }}" class="article-card__link">
|
||||
{{ with .Params.category }}
|
||||
<span class="article-category">{{ . }}</span>
|
||||
{{ end }}
|
||||
|
||||
<h2 class="article-card__title">{{ .Title }}</h2>
|
||||
|
||||
{{ with .Params.subtitle }}
|
||||
<p class="article-card__excerpt">{{ . }}</p>
|
||||
{{ else }}
|
||||
{{ with .Summary }}
|
||||
<p class="article-card__excerpt">{{ . | plainify | truncate 160 }}</p>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<div class="article-card__meta">
|
||||
{{ if .Date }}
|
||||
<time datetime="{{ .Date.Format "2006-01-02" }}">
|
||||
{{ .Date.Format "Jan 2, 2006" }}
|
||||
</time>
|
||||
{{ end }}
|
||||
{{ if .ReadingTime }}
|
||||
<span>{{ .ReadingTime }} min read</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ else }}
|
||||
<p class="text-muted">No articles yet.</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.article-list {
|
||||
display: grid;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
.article-card {
|
||||
background-color: var(--color-bg-elevated);
|
||||
border-radius: var(--radius-md);
|
||||
border: var(--border-width) solid var(--color-border);
|
||||
transition: var(--transition-shadow), var(--transition-transform);
|
||||
}
|
||||
|
||||
.article-card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.article-card__link {
|
||||
display: block;
|
||||
padding: var(--space-6);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.article-card__title {
|
||||
font-size: var(--font-size-xl);
|
||||
margin: var(--space-2) 0 var(--space-3);
|
||||
color: var(--color-text-primary);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.article-card:hover .article-card__title {
|
||||
color: var(--color-accent-cardinal);
|
||||
}
|
||||
|
||||
.article-card__excerpt {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-base);
|
||||
line-height: var(--line-height-relaxed);
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.article-card__meta {
|
||||
display: flex;
|
||||
gap: var(--space-4);
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
</style>
|
||||
{{ end }}
|
||||
69
themes/swissfini/layouts/_default/single.html
Normal file
69
themes/swissfini/layouts/_default/single.html
Normal file
@@ -0,0 +1,69 @@
|
||||
{{ define "main" }}
|
||||
<article class="article" itemscope itemtype="https://schema.org/Article">
|
||||
{{/* Article Header */}}
|
||||
<header class="article-header">
|
||||
{{ with .Params.category }}
|
||||
<span class="article-category" itemprop="articleSection">{{ . }}</span>
|
||||
{{ end }}
|
||||
|
||||
<h1 class="article-title" itemprop="headline">{{ .Title }}</h1>
|
||||
|
||||
{{ with .Params.subtitle }}
|
||||
<p class="article-subtitle" itemprop="description">{{ . }}</p>
|
||||
{{ end }}
|
||||
|
||||
<div class="article-meta">
|
||||
{{ if .Date }}
|
||||
<span class="article-meta-item">
|
||||
<svg class="article-meta-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
|
||||
<line x1="16" y1="2" x2="16" y2="6"></line>
|
||||
<line x1="8" y1="2" x2="8" y2="6"></line>
|
||||
<line x1="3" y1="10" x2="21" y2="10"></line>
|
||||
</svg>
|
||||
<time datetime="{{ .Date.Format "2006-01-02" }}" itemprop="datePublished">
|
||||
{{ .Date.Format "January 2, 2006" }}
|
||||
</time>
|
||||
</span>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Params.source }}
|
||||
<span class="article-meta-item">
|
||||
<svg class="article-meta-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||
<path d="M14 2v6h6"></path>
|
||||
</svg>
|
||||
{{ . }}
|
||||
</span>
|
||||
{{ end }}
|
||||
|
||||
{{ if .ReadingTime }}
|
||||
<span class="article-meta-item">
|
||||
<svg class="article-meta-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
{{ .ReadingTime }} min read
|
||||
</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{/* Article Body */}}
|
||||
<div class="article-body" itemprop="articleBody">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
|
||||
{{/* Tags */}}
|
||||
{{ with .Params.tags }}
|
||||
<footer class="article-footer">
|
||||
<div class="article-tags">
|
||||
<span class="article-tags-label">Tags:</span>
|
||||
{{ range . }}
|
||||
<a href="{{ "tags/" | relURL }}{{ . | urlize }}/" class="article-tag">{{ . }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</footer>
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
36
themes/swissfini/layouts/index.html
Normal file
36
themes/swissfini/layouts/index.html
Normal file
@@ -0,0 +1,36 @@
|
||||
{{ define "main" }}
|
||||
<article class="article">
|
||||
{{/* Article Header */}}
|
||||
<header class="article-header">
|
||||
{{ with .Params.category }}
|
||||
<span class="article-category">{{ . }}</span>
|
||||
{{ end }}
|
||||
|
||||
<h1 class="article-title">{{ .Title }}</h1>
|
||||
|
||||
{{ with .Params.subtitle }}
|
||||
<p class="article-subtitle">{{ . }}</p>
|
||||
{{ end }}
|
||||
|
||||
<div class="article-meta">
|
||||
{{ with .Params.source }}
|
||||
<span class="article-meta-item">
|
||||
<svg class="article-meta-icon" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||
<path d="M14 2v6h6"></path>
|
||||
<path d="M16 13H8"></path>
|
||||
<path d="M16 17H8"></path>
|
||||
<path d="M10 9H8"></path>
|
||||
</svg>
|
||||
{{ . }}
|
||||
</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{/* Article Body */}}
|
||||
<div class="article-body">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</article>
|
||||
{{ end }}
|
||||
160
themes/swissfini/layouts/partials/accessibility-panel.html
Normal file
160
themes/swissfini/layouts/partials/accessibility-panel.html
Normal file
@@ -0,0 +1,160 @@
|
||||
{{/* Accessibility Settings Panel */}}
|
||||
<aside
|
||||
id="accessibility-panel"
|
||||
class="accessibility-panel"
|
||||
aria-hidden="true"
|
||||
aria-labelledby="accessibility-panel-title"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
{{/* Panel Header */}}
|
||||
<div class="accessibility-panel__header">
|
||||
<h2 id="accessibility-panel-title">Accessibility</h2>
|
||||
<button
|
||||
id="accessibility-close"
|
||||
class="accessibility-panel__close"
|
||||
aria-label="Close accessibility settings"
|
||||
>
|
||||
<svg aria-hidden="true" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 6L6 18"></path>
|
||||
<path d="M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{/* Panel Content */}}
|
||||
<div class="accessibility-panel__content">
|
||||
|
||||
{{/* Font Size */}}
|
||||
<fieldset class="accessibility-control">
|
||||
<legend>Text Size</legend>
|
||||
<div class="accessibility-control__buttons">
|
||||
<button
|
||||
id="font-size-decrease"
|
||||
class="btn btn--icon"
|
||||
aria-label="Decrease text size"
|
||||
>
|
||||
<span aria-hidden="true">A−</span>
|
||||
</button>
|
||||
<span
|
||||
id="font-size-label"
|
||||
class="accessibility-control__value"
|
||||
aria-live="polite"
|
||||
>100%</span>
|
||||
<button
|
||||
id="font-size-increase"
|
||||
class="btn btn--icon"
|
||||
aria-label="Increase text size"
|
||||
>
|
||||
<span aria-hidden="true">A+</span>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{/* Color Theme */}}
|
||||
<fieldset class="accessibility-control">
|
||||
<legend>Color Theme</legend>
|
||||
<div class="accessibility-control__buttons" role="group" aria-label="Select color theme">
|
||||
<button data-theme-option="light" class="btn btn--option" aria-pressed="false">
|
||||
Light
|
||||
</button>
|
||||
<button data-theme-option="dark" class="btn btn--option" aria-pressed="false">
|
||||
Dark
|
||||
</button>
|
||||
<button data-theme-option="high-contrast" class="btn btn--option" aria-pressed="false">
|
||||
High Contrast
|
||||
</button>
|
||||
<button data-theme-option="system" class="btn btn--option" aria-pressed="true">
|
||||
System
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{/* Dyslexia Font */}}
|
||||
<fieldset class="accessibility-control">
|
||||
<legend>Reading Support</legend>
|
||||
<button
|
||||
id="dyslexia-toggle"
|
||||
class="btn btn--toggle"
|
||||
aria-pressed="false"
|
||||
role="switch"
|
||||
>
|
||||
<span class="btn__label">Dyslexia-friendly font</span>
|
||||
<span class="btn__state" aria-hidden="true"></span>
|
||||
</button>
|
||||
</fieldset>
|
||||
|
||||
{{/* Line Spacing */}}
|
||||
<fieldset class="accessibility-control">
|
||||
<legend>Line Spacing</legend>
|
||||
<div class="accessibility-control__buttons" role="group" aria-label="Select line spacing">
|
||||
<button data-spacing-option="compact" class="btn btn--option" aria-pressed="false">
|
||||
Compact
|
||||
</button>
|
||||
<button data-spacing-option="normal" class="btn btn--option" aria-pressed="true">
|
||||
Normal
|
||||
</button>
|
||||
<button data-spacing-option="relaxed" class="btn btn--option" aria-pressed="false">
|
||||
Relaxed
|
||||
</button>
|
||||
<button data-spacing-option="loose" class="btn btn--option" aria-pressed="false">
|
||||
Loose
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{/* Reduced Motion */}}
|
||||
<fieldset class="accessibility-control">
|
||||
<legend>Motion</legend>
|
||||
<div class="accessibility-control__buttons" role="group" aria-label="Select motion preference">
|
||||
<button data-motion-option="true" class="btn btn--option" aria-pressed="false">
|
||||
Reduce
|
||||
</button>
|
||||
<button data-motion-option="false" class="btn btn--option" aria-pressed="false">
|
||||
Allow
|
||||
</button>
|
||||
<button data-motion-option="system" class="btn btn--option" aria-pressed="true">
|
||||
System
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{/* Reading Width */}}
|
||||
<fieldset class="accessibility-control">
|
||||
<legend>Reading Width</legend>
|
||||
<div class="accessibility-control__buttons" role="group" aria-label="Select reading width">
|
||||
<button data-width-option="narrow" class="btn btn--option" aria-pressed="false">
|
||||
Narrow
|
||||
</button>
|
||||
<button data-width-option="medium" class="btn btn--option" aria-pressed="true">
|
||||
Medium
|
||||
</button>
|
||||
<button data-width-option="wide" class="btn btn--option" aria-pressed="false">
|
||||
Wide
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
{{/* Enhanced Focus */}}
|
||||
<fieldset class="accessibility-control">
|
||||
<legend>Keyboard Navigation</legend>
|
||||
<button
|
||||
id="enhanced-focus-toggle"
|
||||
class="btn btn--toggle"
|
||||
aria-pressed="false"
|
||||
role="switch"
|
||||
>
|
||||
<span class="btn__label">Enhanced focus indicators</span>
|
||||
<span class="btn__state" aria-hidden="true"></span>
|
||||
</button>
|
||||
</fieldset>
|
||||
|
||||
{{/* Reset Button */}}
|
||||
<div class="accessibility-control accessibility-control--footer">
|
||||
<button id="accessibility-reset" class="btn btn--secondary">
|
||||
Reset to Defaults
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</aside>
|
||||
55
themes/swissfini/layouts/partials/footer.html
Normal file
55
themes/swissfini/layouts/partials/footer.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<footer class="site-footer" role="contentinfo">
|
||||
<div class="footer-container">
|
||||
{{/* Footer Top */}}
|
||||
<div class="footer-top">
|
||||
{{/* Brand */}}
|
||||
<div class="footer-brand">
|
||||
<a href="{{ "/" | relURL }}" class="footer-logo" aria-label="{{ .Site.Title }} - Home">
|
||||
<span class="logo-mark" aria-hidden="true">SF</span>
|
||||
<span class="site-title"><span class="swiss">Swiss</span>Fini.sh</span>
|
||||
</a>
|
||||
<p class="footer-description">
|
||||
{{ .Site.Params.description }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{/* Navigation */}}
|
||||
<div class="footer-section">
|
||||
<h3>Navigation</h3>
|
||||
<ul class="footer-links" role="list">
|
||||
{{ range .Site.Menus.main }}
|
||||
<li><a href="{{ .URL | relURL }}">{{ .Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{/* Resources */}}
|
||||
<div class="footer-section">
|
||||
<h3>Resources</h3>
|
||||
<ul class="footer-links" role="list">
|
||||
<li><a href="{{ "index.xml" | relURL }}">RSS Feed</a></li>
|
||||
<li><a href="#" id="footer-accessibility-link">Accessibility</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/* Footer Bottom */}}
|
||||
<div class="footer-bottom">
|
||||
<p class="footer-copyright">
|
||||
© {{ now.Year }} {{ .Site.Title }}. All facts may be satirical.
|
||||
</p>
|
||||
<ul class="footer-legal" role="list">
|
||||
<li><a href="/privacy/">Privacy</a></li>
|
||||
<li><a href="/terms/">Terms</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Footer accessibility link opens panel
|
||||
document.getElementById('footer-accessibility-link')?.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
document.getElementById('accessibility-toggle')?.click();
|
||||
});
|
||||
</script>
|
||||
116
themes/swissfini/layouts/partials/head.html
Normal file
116
themes/swissfini/layouts/partials/head.html
Normal file
@@ -0,0 +1,116 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
{{/* Title */}}
|
||||
<title>{{ if .IsHome }}{{ .Site.Title }} - {{ .Site.Params.tagline }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}</title>
|
||||
|
||||
{{/* Meta Description */}}
|
||||
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Summary }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}{{ end }}">
|
||||
|
||||
{{/* Author */}}
|
||||
<meta name="author" content="{{ .Site.Params.author }}">
|
||||
|
||||
{{/* Theme Color - Updated by JS based on preferences */}}
|
||||
<meta name="theme-color" content="#faf9f7">
|
||||
|
||||
{{/* Canonical URL */}}
|
||||
<link rel="canonical" href="{{ .Permalink }}">
|
||||
|
||||
{{/* Open Graph / Social */}}
|
||||
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
|
||||
<meta property="og:title" content="{{ .Title }}">
|
||||
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Summary }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}{{ end }}">
|
||||
<meta property="og:url" content="{{ .Permalink }}">
|
||||
<meta property="og:site_name" content="{{ .Site.Title }}">
|
||||
{{ with .Site.Params.ogImage }}<meta property="og:image" content="{{ . | absURL }}">{{ end }}
|
||||
|
||||
{{/* Twitter Card */}}
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="{{ .Title }}">
|
||||
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Summary }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}{{ end }}">
|
||||
|
||||
{{/* RSS */}}
|
||||
{{ range .AlternativeOutputFormats -}}
|
||||
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
|
||||
{{ end -}}
|
||||
|
||||
{{/* Preconnect to Google Fonts */}}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
|
||||
{{/* Stylesheets - Concatenate all CSS files */}}
|
||||
{{ $variables := resources.Get "css/base/_variables.css" }}
|
||||
{{ $reset := resources.Get "css/base/_reset.css" }}
|
||||
{{ $typography := resources.Get "css/base/_typography.css" }}
|
||||
{{ $dark := resources.Get "css/themes/_dark.css" }}
|
||||
{{ $highContrast := resources.Get "css/themes/_high-contrast.css" }}
|
||||
{{ $header := resources.Get "css/components/_header.css" }}
|
||||
{{ $footer := resources.Get "css/components/_footer.css" }}
|
||||
{{ $article := resources.Get "css/components/_article.css" }}
|
||||
{{ $accessibilityPanel := resources.Get "css/components/_accessibility-panel.css" }}
|
||||
{{ $focus := resources.Get "css/utilities/_focus.css" }}
|
||||
{{ $print := resources.Get "css/utilities/_print.css" }}
|
||||
{{ $main := resources.Get "css/main.css" }}
|
||||
|
||||
{{ $allCSS := slice $variables $reset $typography $dark $highContrast $header $footer $article $accessibilityPanel $focus $print $main | resources.Concat "css/bundle.css" | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ $allCSS.RelPermalink }}" integrity="{{ $allCSS.Data.Integrity }}">
|
||||
|
||||
{{/* Favicon */}}
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
|
||||
{{/* Critical inline script to prevent theme flash */}}
|
||||
<script>
|
||||
(function() {
|
||||
try {
|
||||
var stored = localStorage.getItem('swissfini_accessibility');
|
||||
if (stored) {
|
||||
var prefs = JSON.parse(stored);
|
||||
var html = document.documentElement;
|
||||
|
||||
// Theme
|
||||
if (prefs.theme && prefs.theme !== 'light') {
|
||||
if (prefs.theme === 'system') {
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
html.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
} else {
|
||||
html.setAttribute('data-theme', prefs.theme);
|
||||
}
|
||||
}
|
||||
|
||||
// Font size
|
||||
if (prefs.fontSize) {
|
||||
html.setAttribute('data-font-size', prefs.fontSize);
|
||||
}
|
||||
|
||||
// Dyslexia
|
||||
if (prefs.dyslexiaFont) {
|
||||
html.setAttribute('data-dyslexia', 'true');
|
||||
}
|
||||
|
||||
// Line spacing
|
||||
if (prefs.lineSpacing) {
|
||||
html.setAttribute('data-line-spacing', prefs.lineSpacing);
|
||||
}
|
||||
|
||||
// Reading width
|
||||
if (prefs.readingWidth) {
|
||||
html.setAttribute('data-reading-width', prefs.readingWidth);
|
||||
}
|
||||
|
||||
// Enhanced focus
|
||||
if (prefs.enhancedFocus) {
|
||||
html.setAttribute('data-enhanced-focus', 'true');
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
if (prefs.reducedMotion && prefs.reducedMotion !== 'system') {
|
||||
html.setAttribute('data-reduced-motion', prefs.reducedMotion);
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
63
themes/swissfini/layouts/partials/header.html
Normal file
63
themes/swissfini/layouts/partials/header.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<header class="site-header" role="banner">
|
||||
<div class="header-container">
|
||||
{{/* Logo & Site Title */}}
|
||||
<a href="{{ "/" | relURL }}" class="site-logo" aria-label="{{ .Site.Title }} - Home">
|
||||
<span class="logo-mark" aria-hidden="true">SF</span>
|
||||
<div>
|
||||
<span class="site-title"><span class="swiss">Swiss</span>Fini.sh</span>
|
||||
<span class="site-tagline">{{ .Site.Params.tagline }}</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{{/* Mobile Nav Toggle */}}
|
||||
<button
|
||||
class="nav-toggle"
|
||||
aria-expanded="false"
|
||||
aria-controls="main-navigation"
|
||||
aria-label="Toggle navigation menu"
|
||||
>
|
||||
<span class="nav-toggle-icon" aria-hidden="true"></span>
|
||||
</button>
|
||||
|
||||
{{/* Main Navigation */}}
|
||||
<nav id="main-navigation" class="main-nav" aria-label="Main navigation">
|
||||
<ul class="nav-list" role="list">
|
||||
{{ range .Site.Menus.main }}
|
||||
<li class="nav-item">
|
||||
<a
|
||||
href="{{ .URL | relURL }}"
|
||||
class="nav-link{{ if $.IsMenuCurrent "main" . }} active{{ end }}"
|
||||
{{ if $.IsMenuCurrent "main" . }}aria-current="page"{{ end }}
|
||||
>
|
||||
{{ .Name }}
|
||||
</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{{/* Header Actions */}}
|
||||
<div class="header-actions">
|
||||
{{/* Accessibility Toggle */}}
|
||||
<button
|
||||
id="accessibility-toggle"
|
||||
class="accessibility-toggle"
|
||||
aria-expanded="false"
|
||||
aria-controls="accessibility-panel"
|
||||
aria-label="Open accessibility settings"
|
||||
>
|
||||
<svg aria-hidden="true" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path d="M12 1v2"></path>
|
||||
<path d="M12 21v2"></path>
|
||||
<path d="M4.22 4.22l1.42 1.42"></path>
|
||||
<path d="M18.36 18.36l1.42 1.42"></path>
|
||||
<path d="M1 12h2"></path>
|
||||
<path d="M21 12h2"></path>
|
||||
<path d="M4.22 19.78l1.42-1.42"></path>
|
||||
<path d="M18.36 5.64l1.42-1.42"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
3
themes/swissfini/layouts/partials/skip-links.html
Normal file
3
themes/swissfini/layouts/partials/skip-links.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<nav class="skip-links" aria-label="Skip links">
|
||||
<a href="#main-content" class="skip-link">Skip to main content</a>
|
||||
</nav>
|
||||
8
themes/swissfini/layouts/shortcodes/conclusion.html
Normal file
8
themes/swissfini/layouts/shortcodes/conclusion.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{{/* Conclusion Box Shortcode
|
||||
Usage: {{< conclusion >}}Content{{< /conclusion >}}
|
||||
{{< conclusion title="The Bottom Line" >}}Content{{< /conclusion >}}
|
||||
*/}}
|
||||
<div class="conclusion" role="note" aria-label="{{ .Get "title" | default "Conclusion" }}">
|
||||
<h3>{{ .Get "title" | default "The Bottom Line" }}</h3>
|
||||
{{ .Inner | markdownify }}
|
||||
</div>
|
||||
12
themes/swissfini/layouts/shortcodes/irony.html
Normal file
12
themes/swissfini/layouts/shortcodes/irony.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{{/* Irony Box Shortcode
|
||||
Usage: {{< irony >}}Content here{{< /irony >}}
|
||||
{{< irony title="Custom Title" >}}Content{{< /irony >}}
|
||||
*/}}
|
||||
<div class="irony-box" role="note" aria-label="{{ .Get "title" | default "Editorial Note" }}">
|
||||
{{ with .Get "title" }}
|
||||
<strong>{{ . }}:</strong>
|
||||
{{ else }}
|
||||
<strong>Translation:</strong>
|
||||
{{ end }}
|
||||
{{ .Inner | markdownify }}
|
||||
</div>
|
||||
Reference in New Issue
Block a user