first commit
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
<footer class="foot">
|
||||
<div class="wrap">
|
||||
<div class="foot-top-row">
|
||||
<div class="foot-brand">
|
||||
<a href="{{ "/" | relURL }}" class="foot-mark brand" aria-label="CoreDesktop, home">
|
||||
<img src="{{ "images/coredesktop-logo.svg" | relURL }}" alt="CoreDesktop" class="brand-logo" width="873" height="129">
|
||||
</a>
|
||||
<p class="foot-line">Scripts that turn a fresh Arch Linux and GNOME install into a tiling, keyboard-first desktop, ready for work.</p>
|
||||
<span class="foot-place">Built on Arch Linux · MIT licensed</span>
|
||||
</div>
|
||||
|
||||
<div class="foot-grid">
|
||||
{{ range (slice (dict "title" "Guide" "menu" "footer_guide") (dict "title" "The Desktop" "menu" "footer_desktop") (dict "title" "Project" "menu" "footer_project")) }}
|
||||
<nav class="foot-group" aria-label="{{ .title }}">
|
||||
<span class="foot-head">{{ .title }}</span>
|
||||
<ul class="foot-links">
|
||||
{{ range index site.Menus .menu }}
|
||||
{{ $external := hasPrefix .URL "http" }}
|
||||
<li><a href="{{ if $external }}{{ .URL }}{{ else }}{{ .URL | relURL }}{{ end }}">{{ .Name }}{{ if $external }}<span class="arrow" aria-hidden="true">↗</span>{{ end }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="foot-legal">
|
||||
<span class="foot-copy">© {{ now.Format "2006" }} Federated Computer, Inc. CoreDesktop is not a distribution, yet — with gratitude to Arch Linux and to <a href="https://www.youtube.com/@typecraft_dev">Typecraft</a>.</span>
|
||||
<a class="foot-top" href="#top">Back to top<span class="foot-up" aria-hidden="true">↑</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,12 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ if .IsHome }}{{ .Site.Title }} — {{ .Site.Params.tagline }}{{ else }}{{ .Title }} · {{ .Site.Title }}{{ end }}</title>
|
||||
<meta name="description" content="{{ if .Description }}{{ .Description }}{{ else }}{{ .Site.Params.tagline }}{{ end }}">
|
||||
<meta name="theme-color" content="#001a2b">
|
||||
<link rel="icon" href="{{ "favicon.ico" | relURL }}" sizes="32x32">
|
||||
<link rel="icon" href="{{ "favicon.svg" | relURL }}" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="{{ "apple-touch-icon.png" | relURL }}">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ "css/style.css" | relURL }}">
|
||||
@@ -0,0 +1,37 @@
|
||||
{{/*
|
||||
The top bar: logo, the site's pages, and the two actions (source and
|
||||
install). Sticky on every screen; below 1080px the pages fold into a Menu
|
||||
toggle (a <details>, so it works without JS).
|
||||
*/}}
|
||||
{{ $page := . }}
|
||||
<header class="site-head">
|
||||
<div class="wrap head-bar">
|
||||
<a href="{{ "/" | relURL }}" class="brand" aria-label="CoreDesktop, home">
|
||||
<img src="{{ "images/coredesktop-logo.svg" | relURL }}" alt="CoreDesktop" class="brand-logo" width="873" height="129">
|
||||
</a>
|
||||
|
||||
<nav class="head-nav" aria-label="Site">
|
||||
{{ range .Site.Menus.main }}
|
||||
<a href="{{ .URL | relURL }}" class="nav-link"{{ if hasPrefix $page.RelPermalink .URL }} aria-current="page"{{ end }}>{{ .Name }}</a>
|
||||
{{ end }}
|
||||
</nav>
|
||||
|
||||
<div class="head-actions">
|
||||
<a href="{{ site.Params.repoUrl }}" class="head-signin">Source<span class="arrow" aria-hidden="true">↗</span></a>
|
||||
<a href="{{ "/install/" | relURL }}" class="btn btn-primary btn-sm">Install</a>
|
||||
</div>
|
||||
|
||||
<details class="menu">
|
||||
<summary><span class="menu-word">Menu</span><span class="menu-glyph" aria-hidden="true"><i></i><i></i></span></summary>
|
||||
<nav class="menu-panel" aria-label="Site">
|
||||
<a href="{{ "/" | relURL }}" class="menu-top">Home</a>
|
||||
{{ range .Site.Menus.main }}
|
||||
<a href="{{ .URL | relURL }}" class="menu-top">{{ .Name }}</a>
|
||||
{{ end }}
|
||||
<div class="menu-actions">
|
||||
{{ partial "sections/ctas.html" (slice (dict "label" "Install" "url" "/install/" "style" "primary") (dict "label" "Source on Gitea" "url" site.Params.repoUrl "style" "ghost")) }}
|
||||
</div>
|
||||
</nav>
|
||||
</details>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,9 @@
|
||||
{{ range .Params.sections }}
|
||||
{{ if eq .type "hero" }}
|
||||
{{ partial "sections/hero.html" . }}
|
||||
{{ else if eq .type "final-cta" }}
|
||||
{{ partial "sections/final-cta.html" . }}
|
||||
{{ else }}
|
||||
{{ partial "sections/wrapper.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,12 @@
|
||||
{{/*
|
||||
Anchor id for a section: its own `id`, else one made from its `toc`,
|
||||
`kicker`, or `headline`. Used by the section wrapper and by the pricing
|
||||
switch.
|
||||
*/}}
|
||||
{{ $id := "" }}
|
||||
{{ with .id }}
|
||||
{{ $id = . }}
|
||||
{{ else }}
|
||||
{{ with (or .toc .kicker .headline) }}{{ $id = . | plainify | anchorize }}{{ end }}
|
||||
{{ end }}
|
||||
{{ return $id }}
|
||||
@@ -0,0 +1,4 @@
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
{{ with $s.intro }}<div class="prose mb-2">{{ . | markdownify }}</div>{{ end }}
|
||||
<div class="callout"><p>{{ $s.text | markdownify }}</p></div>
|
||||
@@ -0,0 +1,22 @@
|
||||
{{/*
|
||||
A grid of cards: `items`, each with `title`, `body`, optional `label`
|
||||
(a small tag above the title), `image`, and `link` (+ `linktext`).
|
||||
Without an image, `visual: true` draws a gradient panel in its place.
|
||||
*/}}
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<ul class="card-grid{{ with $s.columns }} cols-{{ . }}{{ end }}">
|
||||
{{ range $i, $c := $s.items }}
|
||||
<li class="card{{ if or $c.image $s.visual }} has-visual{{ end }}">
|
||||
{{ with $c.image }}<img class="card-visual" src="{{ . | relURL }}" alt="" loading="lazy">
|
||||
{{ else }}{{ if $s.visual }}<span class="card-visual card-art" style="--i:{{ $i }}" aria-hidden="true"></span>{{ end }}{{ end }}
|
||||
<div class="card-body">
|
||||
{{ with $c.label }}<span class="card-label">{{ . }}</span>{{ end }}
|
||||
<h3 class="card-title">{{ $c.title | markdownify }}</h3>
|
||||
{{ with $c.body }}<p class="card-text">{{ . | markdownify }}</p>{{ end }}
|
||||
{{ with $c.link }}<a class="more card-link" href="{{ . | relURL }}">{{ replaceRE `\s*[→↗]\s*$` "" ($c.linktext | default "Learn more") }}<span class="arrow" aria-hidden="true">→</span></a>{{ end }}
|
||||
</div>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ if $s.ctas }}<div class="cta-row">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,5 @@
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<ul class="check-list">
|
||||
{{ range $s.items }}<li>{{ . | markdownify }}</li>{{ end }}
|
||||
</ul>
|
||||
@@ -0,0 +1,16 @@
|
||||
{{/*
|
||||
A single command in a terminal window, with a Copy button (added by the
|
||||
script in baseof.html). `command` defaults to the site's install
|
||||
command; `prompt` sets the title bar; `note` goes under the window.
|
||||
*/}}
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<figure class="term">
|
||||
<div class="term-bar">
|
||||
<span class="log-dots" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
<span class="term-title">{{ $s.prompt | default "Terminal" }}</span>
|
||||
</div>
|
||||
<pre class="term-body"><code>{{ $s.command | default site.Params.installCommand }}</code></pre>
|
||||
</figure>
|
||||
{{ with $s.note }}<p class="term-note">{{ . | markdownify }}</p>{{ end }}
|
||||
{{ if $s.ctas }}<div class="cta-row mt-2">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,17 @@
|
||||
{{/*
|
||||
Buttons. `primary` is the solid cyan button, `ghost` the outlined
|
||||
secondary, `text` a quiet link with an arrow. Any trailing "→" in a
|
||||
label is dropped — the arrow is drawn.
|
||||
*/}}
|
||||
{{ range . }}
|
||||
{{ $label := replaceRE `\s*[→↗]\s*$` "" .label }}
|
||||
{{ $external := or (hasPrefix .url "http") (hasPrefix .url "mailto:") }}
|
||||
{{ $href := cond (or $external (hasPrefix .url "#")) .url (.url | relURL) }}
|
||||
{{ if eq .style "ghost" }}
|
||||
<a href="{{ $href }}" class="btn btn-secondary">{{ $label }}<span class="arrow" aria-hidden="true">{{ if $external }}↗{{ else }}→{{ end }}</span></a>
|
||||
{{ else if eq .style "text" }}
|
||||
<a href="{{ $href }}" class="more">{{ $label }}<span class="arrow" aria-hidden="true">{{ if $external }}↗{{ else }}→{{ end }}</span></a>
|
||||
{{ else }}
|
||||
<a href="{{ $href }}" class="btn btn-primary">{{ $label }}<span class="arrow" aria-hidden="true">{{ if $external }}↗{{ else }}→{{ end }}</span></a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,11 @@
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<div class="faq-list">
|
||||
{{ range $s.items }}
|
||||
<details class="faq-item">
|
||||
<summary>{{ .q }}</summary>
|
||||
<div class="faq-answer"><p>{{ .a | markdownify }}</p></div>
|
||||
</details>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ if $s.ctas }}<div class="cta-row mt-2">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,8 @@
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
{{ with $s.intro }}<div class="prose mb-2">{{ . | markdownify }}</div>{{ end }}
|
||||
<ul class="feature-list">
|
||||
{{ range $s.items }}
|
||||
<li><strong>{{ .label }}</strong><span>{{ .body | markdownify }}</span></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
@@ -0,0 +1,10 @@
|
||||
{{ $s := . }}
|
||||
<aside class="cta" data-reveal>
|
||||
<div class="wrap cta-inner">
|
||||
<div class="cta-copy">
|
||||
<h2 class="cta-title">{{ $s.headline | markdownify }}</h2>
|
||||
{{ with $s.body }}<p class="cta-text">{{ . | markdownify }}</p>{{ end }}
|
||||
</div>
|
||||
{{ if $s.ctas }}<div class="actions">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
</div>
|
||||
</aside>
|
||||
@@ -0,0 +1,8 @@
|
||||
{{/* Shared section heading: optional kicker (a mono eyebrow), headline, and lede. */}}
|
||||
{{ if or .kicker .headline .lede }}
|
||||
<div class="section-head">
|
||||
{{ with .kicker }}<p class="eyebrow">{{ . }}</p>{{ end }}
|
||||
{{ with .headline }}<h2>{{ . | markdownify }}</h2>{{ end }}
|
||||
{{ with .lede }}<p class="lede">{{ . | markdownify }}</p>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,139 @@
|
||||
{{/*
|
||||
The opening of every page: a full-bleed dark band. On the left, an
|
||||
optional note (the eyebrow), the statement, the dek, the actions; on the
|
||||
right, a terminal typing out an install (when `log:` is set), or the
|
||||
tiles — a three-pane tiled desktop, CoreDesktop's default layout.
|
||||
`visual: false` drops the tiles; `tiles:` relabels the three panes.
|
||||
`video:` (a path without extension, with .webm, .mp4 and .jpg beside it)
|
||||
instead runs a looping screen recording full width under the copy;
|
||||
`videoTitle` sets its window bar and `videoLabel` its description.
|
||||
*/}}
|
||||
{{ $s := . }}
|
||||
{{ $visual := and (not $s.video) (or $s.log (ne $s.visual false)) }}
|
||||
<header class="hero{{ if $s.compact }} hero-compact{{ end }}{{ if $visual }} has-visual{{ end }}">
|
||||
<div class="wrap hero-grid">
|
||||
<div class="hero-copy">
|
||||
{{ with $s.eyebrow }}<p class="note" data-reveal><span class="note-live" aria-hidden="true"></span><span class="note-text">{{ . }}</span></p>{{ end }}
|
||||
<h1 class="statement" data-reveal style="--d:80ms">{{ $s.title | markdownify }}</h1>
|
||||
{{ with $s.subhead }}<p class="dek" data-reveal style="--d:140ms">{{ . | markdownify }}</p>{{ end }}
|
||||
{{ if $s.ctas }}<div class="actions" data-reveal style="--d:200ms">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
</div>
|
||||
|
||||
{{ if not $s.video }}
|
||||
{{ with $s.log }}
|
||||
{{/*
|
||||
A terminal, typed out line by line (see the script below). Lines take
|
||||
`tag` + `text`; `think: true` marks a waiting step (greyed, with a
|
||||
spinner); `done: true` closes the run. The window keeps a fixed height
|
||||
and scrolls as lines arrive.
|
||||
*/}}
|
||||
<figure class="hero-log" data-reveal style="--d:260ms">
|
||||
<div class="log-window">
|
||||
<div class="log-bar">
|
||||
<span class="log-dots" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
<span class="log-title">{{ $s.logTitle | default "~ $ install.sh" }}</span>
|
||||
<span class="log-live"><span class="note-live" aria-hidden="true"></span>Running</span>
|
||||
</div>
|
||||
<div class="log-view">
|
||||
<ol class="log-lines" aria-label="CoreDesktop installing on a fresh Arch Linux system">
|
||||
{{ range . }}
|
||||
<li class="log-line{{ if .done }} is-done{{ end }}{{ if .think }} is-think{{ end }}">
|
||||
<span class="log-tag">{{ .tag | default (cond .done "Done" (cond (.think | default false) "Wait" "")) }}</span>
|
||||
<span class="log-text">{{ .text }}</span>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</figure>
|
||||
<script>
|
||||
(function () {
|
||||
var fig = document.currentScript.previousElementSibling;
|
||||
if (!fig || matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
var view = fig.querySelector('.log-view'), list = fig.querySelector('.log-lines');
|
||||
var lines = [].slice.call(fig.querySelectorAll('.log-line'));
|
||||
var onScreen = true, i = 0;
|
||||
function visible() { return onScreen && !document.hidden; }
|
||||
fig.classList.add('is-animated');
|
||||
lines.forEach(function (li) {
|
||||
li.style.setProperty('--n', li.querySelector('.log-text').textContent.trim().length);
|
||||
});
|
||||
// Pause while the log is off screen or the tab is hidden.
|
||||
function schedule(fn, ms) { setTimeout(function () { if (visible()) fn(); else schedule(fn, 400); }, ms); }
|
||||
function next() {
|
||||
if (i === lines.length) { schedule(reset, 4200); return; }
|
||||
var li = lines[i++], n = +li.style.getPropertyValue('--n');
|
||||
// Finished lines drop their typing animation, so a repaint can't replay it.
|
||||
lines.forEach(function (l) { if (l.classList.contains('is-current')) l.classList.add('is-typed'); l.classList.remove('is-current'); });
|
||||
li.classList.add('is-on', 'is-current');
|
||||
// Keep the newest line in view, like a terminal: slide the list up.
|
||||
var shift = Math.max(0, li.offsetTop + li.offsetHeight + 18 - view.clientHeight);
|
||||
list.style.transform = 'translateY(' + -shift + 'px)';
|
||||
var pause = li.classList.contains('is-think') ? 1300 : li.classList.contains('is-done') ? 300 : 450;
|
||||
schedule(next, n * 16 + pause);
|
||||
}
|
||||
function reset() {
|
||||
fig.classList.add('is-clearing');
|
||||
schedule(function () {
|
||||
lines.forEach(function (l) { l.classList.remove('is-on', 'is-current', 'is-typed'); });
|
||||
list.style.transition = 'none';
|
||||
list.style.transform = '';
|
||||
void list.offsetHeight;
|
||||
list.style.transition = '';
|
||||
fig.classList.remove('is-clearing');
|
||||
i = 0;
|
||||
schedule(next, 500);
|
||||
}, 450);
|
||||
}
|
||||
if ('IntersectionObserver' in window) {
|
||||
new IntersectionObserver(function (e) { onScreen = e[0].isIntersecting; }).observe(fig);
|
||||
}
|
||||
schedule(next, 700);
|
||||
})();
|
||||
</script>
|
||||
{{ else }}
|
||||
{{ if ne $s.visual false }}
|
||||
{{ $tiles := $s.tiles | default (slice "Editor" "Terminal" "Browser") }}
|
||||
<figure class="hero-tiles" aria-hidden="true" data-reveal style="--d:260ms">
|
||||
<div class="tiles-bar"><span>Activities</span><span class="tiles-clock">09:41</span><span class="tiles-grid-icon"><i></i><i></i><i></i></span></div>
|
||||
<div class="tiles-grid">
|
||||
{{ range $i, $t := $tiles }}
|
||||
<span class="tile{{ if eq $i 1 }} is-focus{{ end }}" style="--i:{{ $i }}">
|
||||
<em>{{ printf "%02d" (add $i 1) }} · {{ $t }}</em>
|
||||
<i></i><i></i><i></i>
|
||||
</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="tiles-keys"><kbd>Super</kbd><span>+</span><kbd>←</kbd><kbd>→</kbd><span class="tiles-hint">move between tiles</span></div>
|
||||
</figure>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ with $s.video }}
|
||||
{{/*
|
||||
Muted, looping and inline, so it can autoplay. With reduced motion it
|
||||
stays on the poster, with controls to play it (see the script below).
|
||||
*/}}
|
||||
<figure class="wrap hero-video" data-reveal style="--d:260ms">
|
||||
<div class="video-window">
|
||||
<div class="log-bar">
|
||||
<span class="log-dots" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
<span class="log-title">{{ $s.videoTitle | default "CoreDesktop" }}</span>
|
||||
</div>
|
||||
<video class="video-screen" autoplay muted loop playsinline preload="auto" width="1920" height="540"
|
||||
poster="{{ printf "%s.jpg" . | relURL }}"{{ with $s.videoLabel }} aria-label="{{ . }}"{{ end }}>
|
||||
<source src="{{ printf "%s.webm" . | relURL }}" type="video/webm">
|
||||
<source src="{{ printf "%s.mp4" . | relURL }}" type="video/mp4">
|
||||
</video>
|
||||
</div>
|
||||
</figure>
|
||||
<script>
|
||||
(function () {
|
||||
var v = document.currentScript.previousElementSibling.querySelector('video');
|
||||
if (!v || !matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
v.removeAttribute('autoplay'); v.pause(); v.currentTime = 0; v.controls = true;
|
||||
})();
|
||||
</script>
|
||||
{{ end }}
|
||||
</header>
|
||||
@@ -0,0 +1,33 @@
|
||||
{{/*
|
||||
The sibling project directory, from data/siblings.yaml. Each card shows the
|
||||
project, its author and tags, and the `sibling add` command to install it
|
||||
(with a Copy button, from baseof.html). With no projects listed yet, an
|
||||
empty card invites the first submission: `emptyText` and `emptyCtas`.
|
||||
*/}}
|
||||
{{ $s := . }}
|
||||
{{ $projects := hugo.Data.siblings | default slice }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
{{ if $projects }}
|
||||
<ul class="sib-grid">
|
||||
{{ range sort $projects "name" }}
|
||||
{{ $p := . }}
|
||||
{{ $home := .homepage | default (replaceRE `\.git$` "" .url) }}
|
||||
<li class="sib-card">
|
||||
<div class="sib-head">
|
||||
<h3 class="sib-name"><a href="{{ $home }}">{{ .name }}<span class="arrow" aria-hidden="true">↗</span></a></h3>
|
||||
{{ with $p.author }}<span class="sib-author">by {{ with $p.authorUrl }}<a href="{{ . }}">{{ $p.author }}</a>{{ else }}{{ . }}{{ end }}</span>{{ end }}
|
||||
</div>
|
||||
<p class="sib-desc">{{ .description | markdownify }}</p>
|
||||
{{ with .tags }}<ul class="sib-tags">{{ range . }}<li>{{ . }}</li>{{ end }}</ul>{{ end }}
|
||||
<pre class="sib-cmd"><code>sibling add {{ .url }}</code></pre>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ else }}
|
||||
<div class="sib-empty">
|
||||
<span class="sib-empty-mark" aria-hidden="true"></span>
|
||||
<p class="sib-empty-text">{{ $s.emptyText | default "No sibling projects are listed yet. Yours could be the first." | markdownify }}</p>
|
||||
{{ with $s.emptyCtas }}<div class="actions">{{ partial "sections/ctas.html" . }}</div>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if $s.ctas }}<div class="cta-row">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,14 @@
|
||||
{{/* Two columns, each a heading plus a short list. */}}
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<div class="split">
|
||||
{{ range $s.columns }}
|
||||
<div class="split-col">
|
||||
<h3>{{ .title }}</h3>
|
||||
<ul class="split-list">
|
||||
{{ range .items }}<li>{{ . | markdownify }}</li>{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ if $s.ctas }}<div class="cta-row mt-2">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,45 @@
|
||||
{{/*
|
||||
Stat tiles. Values that start with a number ("80%+", "100+", "30X") count
|
||||
up from zero when the row scrolls into view; others show as written.
|
||||
Without JS or with reduced motion, every value simply shows.
|
||||
*/}}
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<div class="stat-row">
|
||||
{{ range $s.items }}
|
||||
<div class="stat">
|
||||
<span class="stat-value">{{ .value }}</span>
|
||||
<span class="stat-label">{{ .label | markdownify }}</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var row = document.currentScript.previousElementSibling;
|
||||
if (!('IntersectionObserver' in window) || matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
var counters = [];
|
||||
row.querySelectorAll('.stat-value').forEach(function (el) {
|
||||
var m = el.textContent.trim().match(/^(\d+)(.*)$/);
|
||||
if (!m) return;
|
||||
var shown = document.createElement('span'), full = el.textContent.trim();
|
||||
shown.setAttribute('aria-hidden', 'true');
|
||||
shown.textContent = '0' + m[2];
|
||||
el.innerHTML = '<span class="visually-hidden"></span>';
|
||||
el.firstChild.textContent = full;
|
||||
el.appendChild(shown);
|
||||
counters.push({ node: shown, to: +m[1], suffix: m[2] });
|
||||
});
|
||||
if (!counters.length) return;
|
||||
new IntersectionObserver(function (entries, io) {
|
||||
if (!entries[0].isIntersecting) return;
|
||||
io.disconnect();
|
||||
var start = performance.now(), dur = 1200;
|
||||
(function tick(now) {
|
||||
var t = Math.min(1, (now - start) / dur), eased = 1 - Math.pow(1 - t, 3);
|
||||
counters.forEach(function (c) { c.node.textContent = Math.round(c.to * eased) + c.suffix; });
|
||||
if (t < 1) requestAnimationFrame(tick);
|
||||
})(start);
|
||||
}, { threshold: 0.6 }).observe(row);
|
||||
})();
|
||||
</script>
|
||||
{{ if $s.ctas }}<div class="cta-row mt-2">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,14 @@
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<ol class="step-row{{ if eq (len $s.items) 4 }} steps-4{{ end }}">
|
||||
{{ range $i, $item := $s.items }}
|
||||
<li class="step">
|
||||
<div class="step-head">
|
||||
<h3>{{ $item.title }}</h3>
|
||||
<span class="step-num">{{ printf "%03d" (add $i 1) }}</span>
|
||||
</div>
|
||||
<p>{{ $item.body | markdownify }}</p>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ol>
|
||||
{{ if $s.ctas }}<div class="cta-row">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{/*
|
||||
A plain table: `headers` (a list) and `rows` (a list of lists). The first
|
||||
cell of each row is its label. Cells take Markdown.
|
||||
*/}}
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
<div class="table-scroll">
|
||||
<table class="compare-table">
|
||||
<thead>
|
||||
<tr>{{ range $s.headers }}<th scope="col">{{ . }}</th>{{ end }}</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $s.rows }}
|
||||
<tr>
|
||||
{{ range $i, $cell := . }}
|
||||
{{ if eq $i 0 }}<th scope="row">{{ $cell | markdownify }}</th>{{ else }}<td>{{ $cell | markdownify }}</td>{{ end }}
|
||||
{{ end }}
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ if $s.ctas }}<div class="cta-row mt-2">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
@@ -0,0 +1,20 @@
|
||||
{{/*
|
||||
Prose, with optional `ctas`. `offer` adds a highlighted card below:
|
||||
`badge` (a small cyan tag), `text`, and `ctas`.
|
||||
*/}}
|
||||
{{ $s := . }}
|
||||
{{ partial "sections/head.html" $s }}
|
||||
{{/* markdownify drops the <p> around a lone paragraph; put it back so inline markup stays in the flow */}}
|
||||
{{ $body := $s.body | markdownify }}
|
||||
{{ if not (findRE `^\s*<(p|ul|ol|h[1-6]|div|blockquote|pre|table|figure|hr)[\s>]` $body) }}{{ $body = printf "<p>%s</p>" $body | safeHTML }}{{ end }}
|
||||
<div class="prose{{ if $s.centered }} centered{{ end }}">
|
||||
{{ $body }}
|
||||
</div>
|
||||
{{ if $s.ctas }}<div class="cta-row mt-2">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
|
||||
{{ with $s.offer }}
|
||||
<div class="offer-card">
|
||||
{{ with .badge }}<span class="offer-badge">{{ . }}</span>{{ end }}
|
||||
<p class="offer-text">{{ .text | markdownify }}</p>
|
||||
{{ with .ctas }}<div class="offer-actions">{{ partial "sections/ctas.html" . }}</div>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,7 @@
|
||||
{{ $s := . }}
|
||||
{{ $id := partial "section-id.html" $s }}
|
||||
<section class="section section-{{ $s.type }}"{{ with $id }} id="{{ . }}"{{ end }} data-reveal>
|
||||
<div class="wrap">
|
||||
{{ partial (printf "sections/%s.html" $s.type) $s }}
|
||||
</div>
|
||||
</section>
|
||||
Reference in New Issue
Block a user