Initial commit
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
// Demo page behaviour. Not a build input — see src/demo/demo.scss.
|
||||
import '../sass/main.scss'
|
||||
import './demo.scss'
|
||||
|
||||
// -- Colour swatches ---------------------------------------------------------
|
||||
//
|
||||
// Read from the emitted custom properties rather than from a hand-kept list, so
|
||||
// the demo cannot drift from the stylesheet: delete a token and its swatch
|
||||
// disappears on reload.
|
||||
|
||||
function emittedTokens (group) {
|
||||
const names = new Set()
|
||||
for (const sheet of document.styleSheets) {
|
||||
let rules
|
||||
try { rules = sheet.cssRules } catch { continue } // cross-origin
|
||||
for (const rule of rules) {
|
||||
if (rule.selectorText !== ':root') continue
|
||||
for (const prop of rule.style) {
|
||||
if (prop.startsWith(`--tlig-${group}-`)) names.add(prop)
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...names]
|
||||
}
|
||||
|
||||
function renderSwatches () {
|
||||
const root = getComputedStyle(document.documentElement)
|
||||
const html = emittedTokens('color').map((name) => {
|
||||
const value = root.getPropertyValue(name).trim()
|
||||
const short = name.replace('--tlig-color-', '')
|
||||
return `<figure class="swatch">
|
||||
<div class="swatch__chip"><div class="swatch__fill" style="background:${value}"></div></div>
|
||||
<figcaption class="swatch__meta">
|
||||
<div class="swatch__name">${short}</div>
|
||||
<div class="swatch__value">${value}</div>
|
||||
</figcaption>
|
||||
</figure>`
|
||||
}).join('')
|
||||
document.querySelector('#swatches').innerHTML = html
|
||||
document.querySelector('#swatch-count').textContent = emittedTokens('color').length
|
||||
}
|
||||
|
||||
// -- Script switcher ---------------------------------------------------------
|
||||
//
|
||||
// The point of this control: all four papyrus files share one family name and
|
||||
// are separated by unicode-range, so switching scripts changes no CSS at all.
|
||||
// The browser picks the font file by codepoint.
|
||||
|
||||
const SAMPLES = {
|
||||
en: {
|
||||
label: 'English',
|
||||
heading: 'True Life in God',
|
||||
line: 'Peace be with you — I am the Alpha and the Omega.',
|
||||
note: 'Latin — PapyrusLETBold, the original file.',
|
||||
},
|
||||
hu: {
|
||||
// The pangram, not the site's own title: "Igaz Élet Istenben" contains no
|
||||
// codepoint above U+00FF, so it would be served by the Latin file and
|
||||
// would demonstrate nothing. ő (U+0151) and ű (U+0171) are the test.
|
||||
label: 'Magyar',
|
||||
heading: 'Árvíztűrő tükörfúrógép',
|
||||
line: 'Igaz Élet Istenben — ő and ű are what the Latin-Extended cut is for.',
|
||||
note: 'Latin Extended-A — the hu file. The original PapyrusLETBold has no ő or ű.',
|
||||
},
|
||||
bg: {
|
||||
label: 'Български',
|
||||
heading: 'Истински живот в Бога',
|
||||
line: 'Мир на вас — Аз съм Алфа и Омега.',
|
||||
note: 'Cyrillic — the cyrillic and bulgarian cuts.',
|
||||
},
|
||||
ru: {
|
||||
label: 'Русский',
|
||||
heading: 'Истинная жизнь в Боге',
|
||||
line: 'Мир вам — Я есмь Альфа и Омега.',
|
||||
note: 'Cyrillic — same file as Bulgarian, no extra rule.',
|
||||
},
|
||||
el: {
|
||||
label: 'Ελληνικά',
|
||||
heading: 'Αληθινή εν Θεώ Ζωή',
|
||||
line: 'Ειρήνη υμίν — Εγώ ειμι το Άλφα και το Ωμέγα.',
|
||||
note: 'No papyrus cut exists for Greek — this is the fallback face, shown honestly.',
|
||||
},
|
||||
}
|
||||
|
||||
function setScript (code) {
|
||||
const sample = SAMPLES[code]
|
||||
if (!sample) return
|
||||
document.querySelector('#script-heading').textContent = sample.heading
|
||||
document.querySelector('#script-line').textContent = sample.line
|
||||
document.querySelector('#script-note').textContent = sample.note
|
||||
document.querySelector('#script-heading').lang = code
|
||||
for (const button of document.querySelectorAll('[data-script]')) {
|
||||
button.setAttribute('aria-pressed', String(button.dataset.script === code))
|
||||
}
|
||||
}
|
||||
|
||||
// -- Breakpoint readout ------------------------------------------------------
|
||||
|
||||
function trackViewport () {
|
||||
const output = document.querySelector('#viewport')
|
||||
const update = () => {
|
||||
const width = window.innerWidth
|
||||
const name = width >= 1200 ? 'wide' : width >= 800 ? 'tablet' : 'phone'
|
||||
output.textContent = `${width}px · ${name}`
|
||||
}
|
||||
window.addEventListener('resize', update, { passive: true })
|
||||
update()
|
||||
}
|
||||
|
||||
// -- Wire up -----------------------------------------------------------------
|
||||
|
||||
renderSwatches()
|
||||
trackViewport()
|
||||
|
||||
// `?script=bg` opens straight onto a given script, so a particular cut can be
|
||||
// linked to or screenshotted without clicking.
|
||||
const requested = new URLSearchParams(location.search).get('script')
|
||||
setScript(requested in SAMPLES ? requested : 'en')
|
||||
|
||||
for (const button of document.querySelectorAll('[data-script]')) {
|
||||
button.addEventListener('click', () => setScript(button.dataset.script))
|
||||
}
|
||||
|
||||
const boxes = document.querySelector('#toggle-boxes')
|
||||
boxes.addEventListener('click', () => {
|
||||
const on = document.body.classList.toggle('show-boxes')
|
||||
boxes.setAttribute('aria-pressed', String(on))
|
||||
})
|
||||
@@ -0,0 +1,256 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Demo chrome — NOT part of the shipped stylesheet.
|
||||
//
|
||||
// Everything here exists to frame the foundation in something that looks like
|
||||
// the real site (wood ground, papyrus scroll) and to give the page its toolbar
|
||||
// and swatch grid. It is reached only from src/index.html, which is not a build
|
||||
// input, so none of it can reach dist/tlig.css.
|
||||
//
|
||||
// The scroll frame is deliberately here and not in components/: page layout is
|
||||
// still on the "not done" list, and a demo is not the place to quietly ship it.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use '../sass/abstracts' as *;
|
||||
|
||||
$img: '/wp-content/includes/images';
|
||||
|
||||
// -- The page ground ---------------------------------------------------------
|
||||
|
||||
html {
|
||||
background: url('#{$img}/background_planche_1920.jpg') top center / contain fixed;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// The scroll: a head image, a vertically repeating middle, a foot image.
|
||||
.scroll {
|
||||
max-width: 1200px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.scroll__head,
|
||||
.scroll__foot {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.scroll__head {
|
||||
height: 140px;
|
||||
background-image: url('#{$img}/page/background-message-entete_cnd.png.webp');
|
||||
background-position: bottom;
|
||||
}
|
||||
|
||||
.scroll__body {
|
||||
margin-block-start: -1px;
|
||||
padding: 0 clamp(#{space-raw('md')}, 5vw, 5rem) space('xl');
|
||||
background: url('#{$img}/page/background-message-milieux1200x190-4_cnd.png.webp') repeat-y top / 100% auto;
|
||||
}
|
||||
|
||||
.scroll__foot {
|
||||
// The top 18px of this image ramp from alpha 15 up to its 242 plateau — it
|
||||
// fades in. Butted against the body, that ramp composites over bare wood and
|
||||
// reads as a hard line at the body's bottom edge. The overlap below is the
|
||||
// measured ramp height plus a little, so the whole fade lands on papyrus.
|
||||
//
|
||||
// `top` cannot do this: the element is static, and only a positioned box
|
||||
// moves. `position`/`z-index` pin the paint order the effect depends on —
|
||||
// tree order already gives it, but silently.
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-block-start: -20px;
|
||||
height: 114px;
|
||||
background-image: url('#{$img}/page/background-message-footer-1200x114_cnd.png.webp');
|
||||
background-position: top;
|
||||
}
|
||||
|
||||
// Below the tablet breakpoint the site swaps in narrower scroll images, which
|
||||
// are not mirrored into this project. Rather than stretch the wide ones, the
|
||||
// demo falls back to a plain parchment panel.
|
||||
@include down('tablet') {
|
||||
.scroll__head,
|
||||
.scroll__foot { display: none; }
|
||||
|
||||
.scroll__body {
|
||||
margin: space('md');
|
||||
padding: space('lg');
|
||||
border-radius: radius('md');
|
||||
background: rgba(247, 240, 224, 0.92);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Section scaffolding -----------------------------------------------------
|
||||
|
||||
.demo-section {
|
||||
padding-block: space('xl');
|
||||
|
||||
& + & {
|
||||
border-block-start: 1px solid rgba(145, 116, 85, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: space('sm');
|
||||
margin-block-end: space('lg');
|
||||
font-family: font('body');
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: $brown-600;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: currentColor;
|
||||
opacity: 0.35;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-note {
|
||||
margin-block-start: space('sm');
|
||||
font-family: font('body');
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
color: $brown-600;
|
||||
}
|
||||
|
||||
// A row of specimens that should sit side by side and wrap sanely.
|
||||
.demo-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: space('md');
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// -- Swatches ----------------------------------------------------------------
|
||||
|
||||
.swatch-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
gap: space('sm');
|
||||
}
|
||||
|
||||
.swatch {
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(72, 32, 32, 0.15);
|
||||
border-radius: radius('sm');
|
||||
background: $white;
|
||||
font-family: font('body');
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
|
||||
// Checkerboard, so an alpha token reads as translucent rather than as a
|
||||
// slightly different flat colour.
|
||||
&__chip {
|
||||
height: 56px;
|
||||
background-image:
|
||||
linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%),
|
||||
linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%);
|
||||
background-size: 12px 12px;
|
||||
background-position: 0 0, 6px 6px;
|
||||
}
|
||||
|
||||
&__fill { height: 100%; }
|
||||
|
||||
&__meta { padding: space('2xs') space('xs'); }
|
||||
&__name { font-weight: 700; color: $brown-900; }
|
||||
&__value { color: $grey-500; font-variant-numeric: tabular-nums; }
|
||||
}
|
||||
|
||||
// -- Type specimen table -----------------------------------------------------
|
||||
|
||||
.specimen {
|
||||
display: grid;
|
||||
grid-template-columns: 8rem 1fr;
|
||||
gap: space('sm') space('lg');
|
||||
align-items: baseline;
|
||||
|
||||
@include down('phone') {
|
||||
grid-template-columns: 1fr;
|
||||
gap: space('2xs');
|
||||
}
|
||||
}
|
||||
|
||||
.specimen__key {
|
||||
font-family: font('body');
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: $brown-600;
|
||||
}
|
||||
|
||||
// -- Dark-ground demo --------------------------------------------------------
|
||||
|
||||
.demo-dark {
|
||||
padding: space('lg');
|
||||
border-radius: radius('md');
|
||||
background: $brown-900;
|
||||
}
|
||||
|
||||
// -- Toolbar -----------------------------------------------------------------
|
||||
|
||||
.toolbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: space('md');
|
||||
align-items: center;
|
||||
padding: space('xs') space('md');
|
||||
background: rgba(30, 14, 14, 0.94);
|
||||
color: #f0e6d2;
|
||||
font-family: font('body');
|
||||
font-size: 12px;
|
||||
backdrop-filter: blur(6px);
|
||||
|
||||
&__brand {
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&__group {
|
||||
display: flex;
|
||||
gap: space('2xs');
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__spacer { flex: 1; }
|
||||
|
||||
button {
|
||||
padding: 3px 10px;
|
||||
border: 1px solid rgba(240, 230, 210, 0.3);
|
||||
border-radius: radius('pill');
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font-size: 12px;
|
||||
|
||||
&[aria-pressed='true'] {
|
||||
background: #f0e6d2;
|
||||
color: #1e0e0e;
|
||||
border-color: #f0e6d2;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
font-variant-numeric: tabular-nums;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
// "Show the boxes" — outlines every component so the structure is visible.
|
||||
.show-boxes {
|
||||
.site-footer__col,
|
||||
.demo-row > *,
|
||||
.field-group,
|
||||
.scroll__body > * {
|
||||
outline: 1px dashed rgba(202, 74, 31, 0.6);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+228
@@ -0,0 +1,228 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>TLIG — stylesheet demo</title>
|
||||
<script type="module" src="/demo/demo.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="toolbar">
|
||||
<span class="toolbar__brand">tlig.css</span>
|
||||
|
||||
<span class="toolbar__group">
|
||||
<span>Script:</span>
|
||||
<button type="button" data-script="en" aria-pressed="true">English</button>
|
||||
<button type="button" data-script="hu" aria-pressed="false">Magyar</button>
|
||||
<button type="button" data-script="bg" aria-pressed="false">Български</button>
|
||||
<button type="button" data-script="ru" aria-pressed="false">Русский</button>
|
||||
<button type="button" data-script="el" aria-pressed="false">Ελληνικά</button>
|
||||
</span>
|
||||
|
||||
<span class="toolbar__spacer"></span>
|
||||
|
||||
<button type="button" id="toggle-boxes" aria-pressed="false">Show boxes</button>
|
||||
<code id="viewport"></code>
|
||||
</div>
|
||||
|
||||
<div class="scroll">
|
||||
<div class="scroll__head"></div>
|
||||
<div class="scroll__body">
|
||||
|
||||
<!-- ------------------------------------------------------------------ -->
|
||||
<section class="demo-section">
|
||||
<h1>A stylesheet without the jigsaw</h1>
|
||||
<div class="prose">
|
||||
<p>Everything below is rendered by <code>src/sass/main.scss</code> — 10 kB
|
||||
compiled, and every <code>!important</code> in it (four) sits in the
|
||||
one <code>prefers-reduced-motion</code> block, where overriding author
|
||||
animation is the point. <code>tlig_en.css</code> has 518. The wood ground and the papyrus scroll around this
|
||||
text are <em>demo chrome</em>, not part of the stylesheet: page layout
|
||||
is still on the to-do list, and a demo is not the place to quietly
|
||||
ship it.</p>
|
||||
<p>The frame is here anyway because a foundation only tells you the
|
||||
truth when you see it on the ground it has to work on. Brown ink at
|
||||
86% opacity looks arbitrary on white and obviously right on papyrus.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ------------------------------------------------------------------ -->
|
||||
<section class="demo-section">
|
||||
<p class="demo-label">One family name, four files</p>
|
||||
<h1 id="script-heading">True Life in God</h1>
|
||||
<p class="prose" id="script-line"></p>
|
||||
<p class="demo-note" id="script-note"></p>
|
||||
<p class="demo-note">Use the toolbar to switch script. No CSS changes, no
|
||||
language class, no second stylesheet — the four papyrus cuts share the
|
||||
family name <code>papyrus-bold</code> and are separated by
|
||||
<code>unicode-range</code>, so the browser picks the file by codepoint.</p>
|
||||
</section>
|
||||
|
||||
<!-- ------------------------------------------------------------------ -->
|
||||
<section class="demo-section">
|
||||
<p class="demo-label">Type scale</p>
|
||||
<div class="specimen">
|
||||
<span class="specimen__key">h1 · 34px</span>
|
||||
<h1>Papyrus-bold, tight leading</h1>
|
||||
|
||||
<span class="specimen__key">h2 · 22px</span>
|
||||
<h2>Trebuchet, message red, bold</h2>
|
||||
|
||||
<span class="specimen__key">h3 · 20px</span>
|
||||
<h3>Lato bold, heading ink</h3>
|
||||
|
||||
<span class="specimen__key">h4 · 25px</span>
|
||||
<h4>Papyrus-bold, the section mark</h4>
|
||||
|
||||
<span class="specimen__key">h5 · 14px</span>
|
||||
<h5>Lato bold, small heading</h5>
|
||||
|
||||
<span class="specimen__key">h6 · 12px</span>
|
||||
<h6>Lato bold, uppercase</h6>
|
||||
|
||||
<span class="specimen__key">body · 18px</span>
|
||||
<p>Lato at 18px on a 1.5 line height, in
|
||||
<code>--tlig-color-page-ink</code>: a brown at 86% rather than black,
|
||||
so it settles onto the papyrus instead of sitting on top of it. Here
|
||||
is <strong>bold text</strong>, <em>italic text</em>, and
|
||||
<small>small print in the muted token</small>.</p>
|
||||
|
||||
<span class="specimen__key">blockquote</span>
|
||||
<blockquote>The translucent panel — <code>--tlig-color-page-panel</code>
|
||||
at 30% white — with the message-red rule and the asymmetric corner the
|
||||
site has always had.</blockquote>
|
||||
</div>
|
||||
<p class="demo-note">h2 is encoded as Trebuchet / #7a0012 / bold. Avada's
|
||||
own token says Lato / #333333, but <code>tlig_en.css</code> overrides it
|
||||
globally with four <code>!important</code>s — so the override is what
|
||||
actually renders, and the override is what got written down.</p>
|
||||
</section>
|
||||
|
||||
<!-- ------------------------------------------------------------------ -->
|
||||
<section class="demo-section">
|
||||
<p class="demo-label">Links</p>
|
||||
<div class="prose">
|
||||
<p>A <a href="#">default link</a> carries no underline — that is for
|
||||
navigation and cards. Inside <code>.prose</code>, <a href="#">a link
|
||||
underlines itself</a> without needing a class. The explicit
|
||||
<a href="#" class="link-underline">.link-underline</a> variant is kept
|
||||
for templates that ask for it, and <a href="#" class="link-plain">.link-plain</a>
|
||||
inherits its colour, for a link wrapping a whole card.</p>
|
||||
</div>
|
||||
<p class="demo-note">The live rule is
|
||||
<code>a:hover { color: #ca4a1f !important }</code> — one global
|
||||
<code>!important</code> that every later link style then has to fight.
|
||||
That is where most of the 518 in <code>tlig_en.css</code> start.</p>
|
||||
</section>
|
||||
|
||||
<!-- ------------------------------------------------------------------ -->
|
||||
<section class="demo-section">
|
||||
<p class="demo-label">Buttons and fields</p>
|
||||
<div class="demo-row">
|
||||
<a href="#" class="button">View the magazine</a>
|
||||
<a href="#" class="button button--donate">Donate</a>
|
||||
<button class="button" type="button" disabled>Disabled</button>
|
||||
</div>
|
||||
|
||||
<form class="field-group" style="margin-top:1.5rem" onsubmit="return false">
|
||||
<input class="field field--attached" type="email" placeholder="Your email address" aria-label="Email address">
|
||||
<button class="button button--attached" type="submit">Subscribe</button>
|
||||
</form>
|
||||
|
||||
<p class="demo-note">Four ids and classes in the live CSS —
|
||||
<code>#footer-viewMagazineSpan</code>, <code>.btnSubmitNewsletter</code>,
|
||||
<code>.btnSubmitDailyMessage</code>, <code>.footer-donateSpan</code> —
|
||||
repeating the same six declarations with different corner radii. One
|
||||
class with modifiers replaces them.</p>
|
||||
</section>
|
||||
|
||||
<!-- ------------------------------------------------------------------ -->
|
||||
<section class="demo-section">
|
||||
<p class="demo-label">Context re-points tokens</p>
|
||||
<div class="demo-dark on-dark">
|
||||
<h3>The same markup on a dark ground</h3>
|
||||
<p>This block adds one class. It declares no colours of its own: the
|
||||
<code>dark-ground</code> mixin re-points nine tokens, and this
|
||||
heading, this body copy, this <a href="#">link</a>, its hover and the
|
||||
button below are all correct as a result. No dark-specific rule for
|
||||
any of them exists in the stylesheet.</p>
|
||||
<p style="margin-top:1rem"><a href="#" class="button">A button, unchanged</a></p>
|
||||
</div>
|
||||
<p class="demo-note">This is the mechanism that replaces the footer's 24
|
||||
id selectors: a region declares what its tokens mean, and every
|
||||
component inside it is already right. The footer below includes the same
|
||||
mixin — the list of what "dark" means exists once, so the two cannot
|
||||
drift. An earlier draft of this page caught the bug that proves the
|
||||
point: <code>.on-dark</code> had re-pointed the links but not
|
||||
<code>--tlig-color-heading</code>, and the heading above rendered
|
||||
dark-brown on dark-brown.</p>
|
||||
</section>
|
||||
|
||||
<!-- ------------------------------------------------------------------ -->
|
||||
<section class="demo-section">
|
||||
<p class="demo-label">Colour tokens (<span id="swatch-count">0</span>)</p>
|
||||
<div class="swatch-grid" id="swatches"></div>
|
||||
<p class="demo-note">Generated at runtime from the <code>--tlig-color-*</code>
|
||||
properties the stylesheet actually emits, not from a hand-kept list — so
|
||||
this grid cannot drift. Delete a token and its swatch disappears.</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<div class="scroll__foot"></div>
|
||||
</div>
|
||||
|
||||
<!-- The real footer component, not a mock ------------------------------- -->
|
||||
<footer class="site-footer">
|
||||
<div class="site-footer__inner">
|
||||
|
||||
<div class="site-footer__row">
|
||||
<div class="site-footer__col site-footer__col--wide">
|
||||
<p class="site-footer__title">The True Life in God magazine</p>
|
||||
<p class="site-footer__text">Read the latest issue online, or order a
|
||||
printed copy for your prayer group.</p>
|
||||
<p style="margin-top:1rem"><a href="#" class="button">View the magazine</a></p>
|
||||
</div>
|
||||
|
||||
<div class="site-footer__col">
|
||||
<p class="site-footer__title">Newsletter</p>
|
||||
<form class="field-group" onsubmit="return false">
|
||||
<input class="field field--attached" type="email" placeholder="Your email address" aria-label="Email address">
|
||||
<button class="button button--attached" type="submit">Subscribe</button>
|
||||
</form>
|
||||
|
||||
<p class="site-footer__title" style="margin-top:2rem">Daily message</p>
|
||||
<p><a href="#" class="button">Receive it by email</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="site-footer__row">
|
||||
<div class="site-footer__col">
|
||||
<p class="site-footer__title">What is…</p>
|
||||
<ul class="site-footer__menu">
|
||||
<li><a href="#">What is True Life in God?</a></li>
|
||||
<li><a href="#">Who is Vassula Rydén?</a></li>
|
||||
<li><a href="#">The messages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="site-footer__col">
|
||||
<p class="site-footer__title">Support the mission</p>
|
||||
<p><a href="#" class="button button--donate">Donate</a></p>
|
||||
</div>
|
||||
|
||||
<div class="site-footer__col">
|
||||
<p class="site-footer__title">Follow</p>
|
||||
<ul class="site-footer__social">
|
||||
<li><a href="#">Facebook</a></li>
|
||||
<li><a href="#">YouTube</a></li>
|
||||
<li><a href="#">Instagram</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
// Entry point. Its only job today is to pull the stylesheet into the build;
|
||||
// behaviour belongs in src/js/ and gets imported here as it is written.
|
||||
import './sass/main.scss'
|
||||
@@ -0,0 +1,37 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Token accessors.
|
||||
//
|
||||
// These return `var(--tlig-…)` rather than the literal, so a token stays
|
||||
// overridable at runtime. Use `*-raw()` only where a custom property cannot go
|
||||
// — inside a media query condition, or in a `@font-face` block.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use 'sass:map';
|
||||
@use 'tokens' as *;
|
||||
|
||||
@function _var($group, $key, $map) {
|
||||
@if not map.has-key($map, $key) {
|
||||
@error 'Unknown #{$group} token "#{$key}". Known: #{map.keys($map)}.';
|
||||
}
|
||||
@return var(--#{$prefix}-#{$group}-#{$key});
|
||||
}
|
||||
|
||||
@function color($key) { @return _var('color', $key, $colors); }
|
||||
@function font($key) { @return _var('font', $key, $fonts); }
|
||||
@function size($key) { @return _var('size', $key, $font-sizes); }
|
||||
@function leading($key){ @return _var('leading', $key, $line-heights); }
|
||||
@function weight($key) { @return _var('weight', $key, $font-weights); }
|
||||
@function space($key) { @return _var('space', $key, $space); }
|
||||
@function radius($key) { @return _var('radius', $key, $radii); }
|
||||
|
||||
// Compile-time literals, for the few places a custom property is not allowed.
|
||||
@function color-raw($key) { @return map.get($colors, $key); }
|
||||
@function font-raw($key) { @return map.get($fonts, $key); }
|
||||
@function space-raw($key) { @return map.get($space, $key); }
|
||||
|
||||
@function breakpoint($key) {
|
||||
@if not map.has-key($breakpoints, $key) {
|
||||
@error 'Unknown breakpoint "#{$key}". Known: #{map.keys($breakpoints)}.';
|
||||
}
|
||||
@return map.get($breakpoints, $key);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Single import surface for the abstract layer: `@use '../abstracts' as *;`
|
||||
@forward 'palette';
|
||||
@forward 'tokens';
|
||||
@forward 'functions';
|
||||
@forward 'mixins';
|
||||
@@ -0,0 +1,89 @@
|
||||
@use 'sass:map';
|
||||
@use 'tokens' as *;
|
||||
@use 'functions' as *;
|
||||
|
||||
// -- Media queries -----------------------------------------------------------
|
||||
//
|
||||
// Mobile-first: `up()` opens a floor, `down()` a ceiling, and `down()` subtracts
|
||||
// 0.02px so the two never both match on the same viewport. The live CSS has
|
||||
// exactly that overlap bug at 800px.
|
||||
|
||||
@mixin up($name) {
|
||||
@media (min-width: #{breakpoint($name)}) { @content; }
|
||||
}
|
||||
|
||||
@mixin down($name) {
|
||||
@media (max-width: #{breakpoint($name) - 0.02px}) { @content; }
|
||||
}
|
||||
|
||||
@mixin between($from, $to) {
|
||||
@media (min-width: #{breakpoint($from)}) and (max-width: #{breakpoint($to) - 0.02px}) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Type --------------------------------------------------------------------
|
||||
|
||||
@mixin type($family: 'body', $size: 'base', $leading: 'normal', $weight: 'normal') {
|
||||
font-family: font($family);
|
||||
font-size: size($size);
|
||||
line-height: leading($leading);
|
||||
font-weight: weight($weight);
|
||||
}
|
||||
|
||||
// Headings on this site are the display face at a fixed size and tight leading.
|
||||
@mixin heading($size: '2xl') {
|
||||
font-family: font('display');
|
||||
font-size: size($size);
|
||||
line-height: leading('tight');
|
||||
font-weight: weight('normal');
|
||||
color: color('heading');
|
||||
}
|
||||
|
||||
// -- Patterns ----------------------------------------------------------------
|
||||
|
||||
// The translucent panel that carries text over the papyrus background.
|
||||
@mixin panel($pad: 'lg', $round: 'md') {
|
||||
padding: space($pad);
|
||||
background-color: color('page-panel');
|
||||
border-radius: radius($round);
|
||||
}
|
||||
|
||||
// Visible only to screen readers.
|
||||
@mixin visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// A focus ring that survives on both the light page and the dark footer.
|
||||
@mixin focus-ring($offset: 2px) {
|
||||
outline: 2px solid currentColor;
|
||||
outline-offset: $offset;
|
||||
}
|
||||
|
||||
// Re-point the token set for a dark ground.
|
||||
//
|
||||
// This is the whole contract in one place: any region that includes it gets
|
||||
// correct body copy, headings, links and hovers with no further declarations.
|
||||
// It must cover *every* token that encodes a light-ground assumption — an
|
||||
// incomplete list is how a heading ends up dark-brown on dark-brown.
|
||||
@mixin dark-ground {
|
||||
--#{$prefix}-color-page-ink: #{map.get($colors, 'footer-ink')};
|
||||
--#{$prefix}-color-page-ink-strong: #{map.get($colors, 'footer-ink')};
|
||||
--#{$prefix}-color-page-ink-muted: #{map.get($colors, 'footer-rule')};
|
||||
--#{$prefix}-color-heading: #{map.get($colors, 'footer-ink')};
|
||||
--#{$prefix}-color-heading-message: #{map.get($colors, 'footer-link-hover')};
|
||||
--#{$prefix}-color-link: #{map.get($colors, 'footer-link')};
|
||||
--#{$prefix}-color-link-hover: #{map.get($colors, 'footer-link-hover')};
|
||||
--#{$prefix}-color-link-underlined: #{map.get($colors, 'footer-link')};
|
||||
--#{$prefix}-color-page-rule: #{map.get($colors, 'footer-rule')};
|
||||
|
||||
color: color('page-ink');
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Raw palette — every literal colour the live site actually uses.
|
||||
//
|
||||
// Nothing outside this file may write a hex value. Rules consume the *semantic*
|
||||
// tokens in `_tokens.scss`, never these names directly: a raw name says what a
|
||||
// colour looks like, a semantic name says what it is for, and only the second
|
||||
// survives a redesign.
|
||||
//
|
||||
// Source: `Avada-Child-Theme/style.css` (`:root` fusion variables) and
|
||||
// `Avada-Child-Theme/css/tlig_en.css`, September 2026.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Browns — the site's "ink"; the wood background and papyrus scroll sit under
|
||||
// everything, so the darkest text tone is a brown, never black.
|
||||
$brown-900: #482020; // 74 uses in tlig_en.css — the most common colour
|
||||
$brown-800: #511a06; // message body text
|
||||
$brown-600: #917455; // wood / border tone
|
||||
$brown-300: #c8b29a; // newsletter input field
|
||||
|
||||
// Reds — link, heading and call-to-action family.
|
||||
$red-900: #7a0012; // message headings (h2)
|
||||
$red-800: #810008; // Avada --link_color
|
||||
$red-780: #7e050c; // .linkUnderline
|
||||
$red-700: #8f0b06; // primary button fill
|
||||
$red-650: #770000; // mobile header / menu
|
||||
$red-600: #951111; // blockquote rule
|
||||
$red-500: #961a07; // alert
|
||||
$red-400: #ca4a1f; // link hover (global `a:hover` in tlig_en.css)
|
||||
|
||||
// Golds — footer, accents, hover states on dark ground.
|
||||
$gold-700: #d8ae0b; // donate button
|
||||
$gold-600: #ffc600; // sliding-bar link hover
|
||||
$gold-500: #f9d368; // footer dropdown rules
|
||||
$gold-400: #ffe683; // hover on dark
|
||||
$gold-300: #ffe7bf; // mobile menu hover
|
||||
$gold-200: #fff8c4; // mobile menu text
|
||||
$gold-100: #fbefc9; // footer text
|
||||
|
||||
// Neutrals.
|
||||
$white: #ffffff;
|
||||
$grey-050: #fbfbfb;
|
||||
$grey-100: #f0f0f0;
|
||||
$grey-200: #eaeaea;
|
||||
$grey-300: #d6d6d6;
|
||||
$grey-400: #bbbbbb;
|
||||
$grey-500: #989898;
|
||||
$grey-700: #333333;
|
||||
$black: #0a0a0a;
|
||||
|
||||
// Semantic accents used by notices and status chips.
|
||||
$green-600: #53961e;
|
||||
$green-400: #96d865;
|
||||
$blue-700: #244c80;
|
||||
$sand-100: #fcf8e3;
|
||||
|
||||
// Alpha tints. Kept as literals rather than computed, because these exact
|
||||
// values are what ships today and a rounding difference would be a visible
|
||||
// change dressed up as a refactor.
|
||||
$ink-86a: rgba(72, 32, 32, 0.86); // body text
|
||||
$ink-50a: rgba(72, 32, 32, 0.5);
|
||||
$wood-50a: rgba(145, 116, 85, 0.5);
|
||||
$wood-20a: rgba(145, 116, 85, 0.2);
|
||||
$white-80a: rgba(255, 255, 255, 0.8);
|
||||
$white-30a: rgba(255, 255, 255, 0.3); // the translucent "paper" panels
|
||||
$shadow-35a: rgba(0, 0, 0, 0.35);
|
||||
$shadow-15a: rgba(0, 0, 0, 0.15);
|
||||
@@ -0,0 +1,139 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Semantic tokens.
|
||||
//
|
||||
// One map per concern. Each map is emitted as CSS custom properties by
|
||||
// `base/_root.scss`, so a token can be re-pointed per language or per section
|
||||
// at runtime without recompiling — which is how the per-language papyrus and
|
||||
// the dark footer get handled without a second stylesheet.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use 'palette' as *;
|
||||
|
||||
$prefix: 'tlig';
|
||||
|
||||
// -- Colour ------------------------------------------------------------------
|
||||
|
||||
$colors: (
|
||||
// Page
|
||||
'page-ink': $ink-86a, // body copy
|
||||
'page-ink-strong': $brown-900, // headings, emphasis
|
||||
'page-ink-muted': $grey-500,
|
||||
'page-panel': $white-30a, // translucent card over the scroll
|
||||
'page-rule': $grey-300,
|
||||
|
||||
// Links
|
||||
'link': $red-800,
|
||||
'link-hover': $red-400,
|
||||
'link-underlined': $red-780, // the `.link-underline` variant
|
||||
|
||||
// Headings
|
||||
'heading': $brown-900,
|
||||
'heading-message': $red-900, // h2 inside a message
|
||||
|
||||
// Message typography (the scroll pages)
|
||||
'message-body': $brown-800,
|
||||
'message-aside': $brown-600,
|
||||
|
||||
// Footer — the one region on a dark ground
|
||||
'footer-bg': $brown-900,
|
||||
'footer-ink': $gold-100,
|
||||
'footer-link': $white,
|
||||
'footer-link-hover': $gold-400,
|
||||
'footer-rule': $gold-500,
|
||||
'footer-panel': $black,
|
||||
|
||||
// Buttons
|
||||
'button-bg': $red-700,
|
||||
'button-ink': $white,
|
||||
'button-ink-hover': $gold-400,
|
||||
'button-donate-bg': $gold-700,
|
||||
'button-donate-ink': $white,
|
||||
|
||||
// Forms
|
||||
'field-bg': $brown-300,
|
||||
'field-ink': $white,
|
||||
'field-placeholder': rgba(255, 255, 255, 0.8),
|
||||
|
||||
// Status
|
||||
'ok': $green-600,
|
||||
'ok-soft': $green-400,
|
||||
'alert': $red-500,
|
||||
'info': $blue-700,
|
||||
'notice-bg': $sand-100,
|
||||
|
||||
// Chrome
|
||||
'shadow': $shadow-35a,
|
||||
'shadow-soft': $shadow-15a,
|
||||
);
|
||||
|
||||
// -- Type --------------------------------------------------------------------
|
||||
|
||||
// `papyrus-bold` is the display face; the per-script files are bound to the
|
||||
// same family name via unicode-range in `base/_fonts.scss`, so a Bulgarian
|
||||
// heading needs no separate rule.
|
||||
$fonts: (
|
||||
'display': (papyrus-bold, 'Papyrus', fantasy),
|
||||
'body': (Lato, 'Trebuchet MS', Helvetica, sans-serif),
|
||||
'message': ('Trebuchet MS', Verdana, sans-serif),
|
||||
'nav': (Oswald, 'Lato', sans-serif),
|
||||
);
|
||||
|
||||
$font-sizes: (
|
||||
'xs': 12px,
|
||||
'sm': 14px,
|
||||
'base': 18px, // Avada --body_typography-font-size
|
||||
'md': 20px,
|
||||
'lg': 22px,
|
||||
'xl': 25px,
|
||||
'2xl': 34px, // h1
|
||||
);
|
||||
|
||||
$line-heights: (
|
||||
'tight': 1.2, // headings
|
||||
'snug': 1.35,
|
||||
'normal': 1.5, // body
|
||||
'message': 1.33, // 24px on 18px
|
||||
);
|
||||
|
||||
$font-weights: (
|
||||
'normal': 400,
|
||||
'medium': 600,
|
||||
'bold': 700,
|
||||
);
|
||||
|
||||
// -- Space -------------------------------------------------------------------
|
||||
|
||||
// A 4px-based scale. The live CSS uses arbitrary values (6px, 23px, 46px…);
|
||||
// those are rounded to the nearest step here. Every such rounding is invisible
|
||||
// at a glance and is the point of the exercise — one rhythm, not forty.
|
||||
$space: (
|
||||
'3xs': 2px,
|
||||
'2xs': 4px,
|
||||
'xs': 8px,
|
||||
'sm': 12px,
|
||||
'md': 16px,
|
||||
'lg': 24px,
|
||||
'xl': 32px,
|
||||
'2xl': 48px,
|
||||
'3xl': 64px,
|
||||
);
|
||||
|
||||
$radii: (
|
||||
'sm': 5px,
|
||||
'md': 7px,
|
||||
'lg': 20px,
|
||||
'pill': 999px,
|
||||
);
|
||||
|
||||
// -- Layout ------------------------------------------------------------------
|
||||
|
||||
$site-width: 1200px; // Avada --site_width
|
||||
|
||||
// Named breakpoints. The live CSS switches at 480 and 799/800 with a one-pixel
|
||||
// gap between `max-width: 800px` and `min-width: 800px` rules; that gap is
|
||||
// closed here by driving everything from these two numbers.
|
||||
$breakpoints: (
|
||||
'phone': 480px,
|
||||
'tablet': 800px,
|
||||
'wide': 1200px,
|
||||
);
|
||||
@@ -0,0 +1,85 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// @font-face.
|
||||
//
|
||||
// The live site declares papyrus-bold once, in the compiled Avada stylesheet,
|
||||
// pointing at absolute ww3.tlig.org URLs — which is why a staging copy renders
|
||||
// with the fallback face. Here the files are project assets: Vite fingerprints
|
||||
// them and rewrites the URL, so the bundle is self-contained wherever it lands.
|
||||
//
|
||||
// All four papyrus files share one family name and are separated by
|
||||
// unicode-range. A Bulgarian or Hungarian heading therefore needs no language
|
||||
// class and no second stylesheet — the browser picks the file by codepoint.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Latin — the original PapyrusLETBold.
|
||||
@font-face {
|
||||
font-family: papyrus-bold;
|
||||
src: url('/fonts/papyrus/papyrus-bold-latin.woff2') format('woff2'),
|
||||
url('/fonts/papyrus/papyrus-bold-latin.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
|
||||
U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122,
|
||||
U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
// Latin Extended-A — Hungarian needs ő and ű, which the original file lacks.
|
||||
@font-face {
|
||||
font-family: papyrus-bold;
|
||||
src: url('/fonts/papyrus/papyrus-bold-hu.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
// Cyrillic.
|
||||
@font-face {
|
||||
font-family: papyrus-bold;
|
||||
src: url('/fonts/papyrus/papyrus-bold-cyrillic.woff2') format('woff2'),
|
||||
url('/fonts/papyrus/papyrus-bold-cyrillic.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
// Cyrillic Extended — the Bulgarian cut covers the forms the base file misses.
|
||||
@font-face {
|
||||
font-family: papyrus-bold;
|
||||
src: url('/fonts/papyrus/papyrus-bold-bulgarian.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
|
||||
// Lato — self-hosted. The live site pulls this from Google via Avada's
|
||||
// fusion-gfonts cache, 33 @font-face blocks for what is three files.
|
||||
@font-face {
|
||||
font-family: Lato;
|
||||
src: url('/fonts/lato/Lato-Medium.woff2') format('woff2'),
|
||||
url('/fonts/lato/Lato-Medium.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Lato;
|
||||
src: url('/fonts/lato/Lato-MediumItalic.woff2') format('woff2'),
|
||||
url('/fonts/lato/Lato-MediumItalic.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Lato;
|
||||
src: url('/fonts/lato/Lato-Bold.woff2') format('woff2'),
|
||||
url('/fonts/lato/Lato-Bold.woff') format('woff');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
@forward 'root';
|
||||
@forward 'fonts';
|
||||
@forward 'reset';
|
||||
@forward 'typography';
|
||||
@forward 'links';
|
||||
@@ -0,0 +1,51 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Links.
|
||||
//
|
||||
// The live rule is `a:hover { color: #ca4a1f !important; }` — one global
|
||||
// `!important` that every later link style then has to fight, which is where
|
||||
// most of the `!important` inflation in `tlig_en.css` starts. Here the hover
|
||||
// colour is inherited from a token, so a context that needs a different hover
|
||||
// (the footer, a button) simply re-points `--tlig-color-link-hover`.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use '../abstracts' as *;
|
||||
|
||||
a {
|
||||
color: color('link');
|
||||
text-decoration: none;
|
||||
transition: color 120ms ease-in-out;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: color('link-hover');
|
||||
}
|
||||
}
|
||||
|
||||
// Body copy inside prose gets an underline; navigation and cards do not.
|
||||
.prose a,
|
||||
.link-underline {
|
||||
color: color('link-underlined');
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.15em;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: color('link-hover');
|
||||
}
|
||||
}
|
||||
|
||||
// A link wrapping a card or an image should not tint its contents.
|
||||
.link-plain {
|
||||
color: inherit;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
// Any region that re-points the token set gets correct links for free — this
|
||||
// is what the footer does, via the same mixin.
|
||||
.on-dark {
|
||||
@include dark-ground;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Reset. Deliberately small — it normalises box model and defaults, and stops.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use '../abstracts' as *;
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// Headings and paragraphs carry their spacing from the type scale, not from
|
||||
// whatever the user agent thinks.
|
||||
h1, h2, h3, h4, h5, h6,
|
||||
p, figure, blockquote, dl, dd {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:focus-visible {
|
||||
@include focus-ring;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Emit every token as a CSS custom property.
|
||||
//
|
||||
// This is the only place the SASS maps become CSS. Everything downstream reads
|
||||
// `var(--tlig-…)`, which means a language, a section or a future dark mode can
|
||||
// re-point a token in one declaration block instead of re-stating the rules.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use 'sass:map';
|
||||
@use 'sass:list';
|
||||
@use '../abstracts' as *;
|
||||
|
||||
:root {
|
||||
@each $key, $value in $colors { --#{$prefix}-color-#{$key}: #{$value}; }
|
||||
@each $key, $value in $fonts { --#{$prefix}-font-#{$key}: #{list.join((), $value, $separator: comma)}; }
|
||||
@each $key, $value in $font-sizes { --#{$prefix}-size-#{$key}: #{$value}; }
|
||||
@each $key, $value in $line-heights{ --#{$prefix}-leading-#{$key}: #{$value}; }
|
||||
@each $key, $value in $font-weights{ --#{$prefix}-weight-#{$key}: #{$value}; }
|
||||
@each $key, $value in $space { --#{$prefix}-space-#{$key}: #{$value}; }
|
||||
@each $key, $value in $radii { --#{$prefix}-radius-#{$key}: #{$value}; }
|
||||
|
||||
--#{$prefix}-site-width: #{$site-width};
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Element typography.
|
||||
//
|
||||
// Sizes and colours are the ones the site renders today; the difference is that
|
||||
// each is stated once, from a token, with no `!important`. `tlig_en.css` sets
|
||||
// h2 globally with four `!important`s to beat Avada's own h2 rule — here there
|
||||
// is only one h2 rule, so nothing needs beating.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use '../abstracts' as *;
|
||||
|
||||
body {
|
||||
@include type('body', 'base', 'normal');
|
||||
color: color('page-ink');
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@include heading('2xl');
|
||||
margin-block: space('md') space('xs');
|
||||
}
|
||||
|
||||
// Trebuchet, deep red, bold: the message-heading look, applied globally today.
|
||||
h2 {
|
||||
font-family: font('message');
|
||||
font-size: size('lg');
|
||||
line-height: leading('normal');
|
||||
font-weight: weight('bold');
|
||||
color: color('heading-message');
|
||||
margin-block-end: space('sm');
|
||||
}
|
||||
|
||||
h3 {
|
||||
@include type('body', 'md', 'normal', 'bold');
|
||||
color: color('heading');
|
||||
margin-block-end: space('xs');
|
||||
}
|
||||
|
||||
h4 {
|
||||
@include heading('xl');
|
||||
margin-block: space('lg');
|
||||
}
|
||||
|
||||
h5 {
|
||||
@include type('body', 'sm', 'normal', 'bold');
|
||||
color: color('heading');
|
||||
}
|
||||
|
||||
h6 {
|
||||
@include type('body', 'xs', 'normal', 'bold');
|
||||
color: color('heading');
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-block-end: space('md');
|
||||
|
||||
&:last-child {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
}
|
||||
|
||||
strong, b { font-weight: weight('bold'); }
|
||||
em, i { font-style: italic; }
|
||||
|
||||
small {
|
||||
font-size: size('sm');
|
||||
color: color('page-ink-muted');
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
margin-block: space('xl');
|
||||
border: 0;
|
||||
background-color: color('page-rule');
|
||||
}
|
||||
|
||||
blockquote {
|
||||
@include panel('lg', 'lg');
|
||||
border-inline-start: 4px solid color('heading-message');
|
||||
border-end-start-radius: space('2xl');
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
// -- Utility classes ---------------------------------------------------------
|
||||
//
|
||||
// The live CSS carries `.font-papyrus`, `.font34`, `.centre`, `.bold`, `.ita`
|
||||
// and friends, each an `!important` one-liner. Those are template-facing, so
|
||||
// they are kept — but as plain, unforced declarations.
|
||||
|
||||
.font-display { font-family: font('display'); }
|
||||
.font-body { font-family: font('body'); }
|
||||
.font-message { font-family: font('message'); }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.text-start { text-align: start; }
|
||||
.text-justify { text-align: justify; }
|
||||
|
||||
.text-muted { color: color('page-ink-muted'); }
|
||||
.text-strong { color: color('page-ink-strong'); }
|
||||
|
||||
.visually-hidden { @include visually-hidden; }
|
||||
@@ -0,0 +1,59 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Buttons.
|
||||
//
|
||||
// The live site has `#footer-viewMagazineSpan`, `.btnSubmitNewsletter`,
|
||||
// `.btnSubmitDailyMessage` and `.footer-donateSpan` — four ids and classes
|
||||
// repeating the same six declarations with different corner radii. One class
|
||||
// with modifiers replaces them.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use '../abstracts' as *;
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: space('xs') space('md');
|
||||
border: 1px solid transparent;
|
||||
border-radius: radius('sm');
|
||||
background-color: color('button-bg');
|
||||
color: color('button-ink');
|
||||
font-family: font('body');
|
||||
font-size: size('sm');
|
||||
font-weight: weight('bold');
|
||||
line-height: 1.2;
|
||||
letter-spacing: 2px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
transition: color 120ms ease-in-out, background-color 120ms ease-in-out;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: color('button-ink-hover');
|
||||
}
|
||||
|
||||
// Donate: gold ground, ink on hover rather than gold-on-gold.
|
||||
&--donate {
|
||||
background-color: color('button-donate-bg');
|
||||
color: color('button-donate-ink');
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: color('page-ink-strong');
|
||||
}
|
||||
}
|
||||
|
||||
// The half of a joined field + button pair.
|
||||
&--attached {
|
||||
border-start-start-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
}
|
||||
|
||||
&--block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Text inputs. Modelled on the footer newsletter field (`.inputEmail`), which
|
||||
// today carries five `!important`s to override Avada's form styling.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use '../abstracts' as *;
|
||||
|
||||
.field {
|
||||
min-width: 0;
|
||||
padding: space('2xs') space('xs');
|
||||
border: 1px solid color('field-bg');
|
||||
border-radius: radius('sm');
|
||||
background-color: color('field-bg');
|
||||
color: color('field-ink');
|
||||
font-family: font('body');
|
||||
font-size: size('sm');
|
||||
letter-spacing: 2px;
|
||||
|
||||
&::placeholder {
|
||||
color: color('field-placeholder');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&--attached {
|
||||
border-start-end-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Field + button as one control.
|
||||
.field-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.field { flex: 1 1 auto; max-width: 18rem; }
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Footer.
|
||||
//
|
||||
// The live footer is 24 id selectors (`#footer-magazine`, `#footer-rightblock`,
|
||||
// `#footer-whatOptionLink1`…) with hand-tuned percentage widths and a media
|
||||
// query that resets each one to 100%. This is the same layout expressed as two
|
||||
// flex containers, and it re-points the link tokens once via `.on-dark` instead
|
||||
// of colouring each link by hand.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use '../abstracts' as *;
|
||||
|
||||
.site-footer {
|
||||
// Scope the dark-ground tokens to the whole region. Everything inside —
|
||||
// links, headings, buttons — reads the right colour without a single
|
||||
// footer-specific colour declaration. Shared with `.on-dark` so the two can
|
||||
// never drift apart.
|
||||
@include dark-ground;
|
||||
|
||||
padding-block: space('xl');
|
||||
background-color: color('footer-bg');
|
||||
font-family: font('display');
|
||||
}
|
||||
|
||||
.site-footer__inner {
|
||||
width: min(100% - 2 * #{space-raw('lg')}, var(--#{$prefix}-site-width));
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
// Two rows: magazine + newsletter above, the three link columns below.
|
||||
.site-footer__row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: space('xl');
|
||||
justify-content: space-between;
|
||||
|
||||
& + & {
|
||||
margin-block-start: space('2xl');
|
||||
}
|
||||
}
|
||||
|
||||
.site-footer__col {
|
||||
flex: 1 1 18rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// The magazine block is the wide one at desktop; below the tablet breakpoint
|
||||
// every column is simply full width, which the flex-basis already gives us.
|
||||
.site-footer__col--wide {
|
||||
flex: 1 1 28rem;
|
||||
}
|
||||
|
||||
.site-footer__title {
|
||||
font-family: font('display');
|
||||
font-size: size('md');
|
||||
line-height: leading('tight');
|
||||
color: color('footer-ink');
|
||||
margin-block-end: space('sm');
|
||||
}
|
||||
|
||||
.site-footer__text {
|
||||
font-size: size('md');
|
||||
line-height: leading('snug');
|
||||
}
|
||||
|
||||
// Social row.
|
||||
.site-footer__social {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: space('md');
|
||||
justify-content: center;
|
||||
list-style: none;
|
||||
|
||||
a {
|
||||
font-size: size('xl');
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: color('button-donate-bg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The "what is…" dropdown: a bordered list on the dark panel colour.
|
||||
.site-footer__menu {
|
||||
// 80% of the column, as the live `#footer-whatDropdown` has it, but capped so
|
||||
// it does not stretch on a wide screen.
|
||||
max-width: min(22rem, 80%);
|
||||
margin-inline: auto;
|
||||
list-style: none;
|
||||
text-align: start;
|
||||
border: 1px solid color('footer-rule');
|
||||
border-radius: radius('sm');
|
||||
overflow: hidden;
|
||||
|
||||
li + li {
|
||||
border-block-start: 1px solid color('footer-rule');
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: space('xs') space('sm');
|
||||
background-color: color('footer-panel');
|
||||
}
|
||||
}
|
||||
|
||||
@include down('tablet') {
|
||||
.site-footer__row {
|
||||
gap: space('lg');
|
||||
}
|
||||
|
||||
.site-footer__col,
|
||||
.site-footer__col--wide {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@forward 'button';
|
||||
@forward 'field';
|
||||
@forward 'footer';
|
||||
@@ -0,0 +1,14 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// TLIG stylesheet — entry point.
|
||||
//
|
||||
// Layer order is load-bearing: tokens first, then element defaults, then
|
||||
// components. Nothing later in the file needs higher specificity than anything
|
||||
// earlier, which is the whole reason there is not a single `!important` here.
|
||||
//
|
||||
// abstracts/ compile-time only, emits no CSS
|
||||
// base/ custom properties, @font-face, reset, element typography, links
|
||||
// components/ single-purpose blocks
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@use 'base';
|
||||
@use 'components';
|
||||
Reference in New Issue
Block a user