From 251bec598b683011912f59334964a88e8783fe21 Mon Sep 17 00:00:00 2001 From: The OC Guy Date: Fri, 23 Oct 2026 14:41:37 +0000 Subject: [PATCH] Rebuild homepage as an editorial page after the VITA reference Replaces the navy/pink direction with the reference's own language: paper ground, black ink, hairline rules, Playfair Display for display, Inter tracked caps for labels, italic serif for dates, boxed modules, three-column card grid, black rectangular buttons. Sections in the required order: 1. Latest Tips and Tricks - three clickable cards, category band, serif headline, description, reading time, View all tips. 2. Featured Module - Inventory in a bordered plate: manifest facts, description, three benefits, Explore module CTA. 3. Latest News from the Community - three rows with date, headline, excerpt and source, pulled from the OCA repositories. Nav, welcome copy, mail form, contact address and picture credits preserved. 44px touch targets on phones, visible focus rings. --- css/site.css | 1153 +++++++++++++++++++++++--------------------------- index.html | 332 +++++++-------- js/site.js | 156 ++----- 3 files changed, 729 insertions(+), 912 deletions(-) diff --git a/css/site.css b/css/site.css index cfb9c53..ac165ac 100644 --- a/css/site.css +++ b/css/site.css @@ -1,735 +1,656 @@ /* ============================================================ - The OC Guy — Odoo Community tips and tricks - Direction: "the sentence is the layout" (seed 1084447843) - Palette: navy field, pink ink. No Odoo plum, no teal, no violet. + The OC Guy — editorial rebuild + Design language from the VITA reference: paper ground, black + ink, hairline rules, didone serif display, tiny tracked caps + for labels, italic serif meta, boxed modules, 3-column cards. ============================================================ */ :root { - /* colour roles */ - --bg: #1a2039; - --surface: #212846; - --ink: #f9a3c0; - --muted: #b9879c; - --accent: #f9a3c0; - --accent-ink: #1a2039; + --paper: #fbfaf7; + --ink: #111110; + --muted: #6c6a66; + --rule: rgba(17, 17, 16, 0.16); + --rule-strong: rgba(17, 17, 16, 0.42); + --tint: #f2f0ec; - /* type steps — five, no more */ - --t-small: clamp(13px, 0.8rem + 0.1vw, 14px); - --t-body: clamp(17px, 1rem + 0.25vw, 19px); - --t-statement: clamp(26px, 2.35vw, 36px); - --t-head: clamp(32px, 1.3rem + 3.2vw, 64px); - --t-display: clamp(38px, 6.1vw, 90px); + --serif: "Playfair Display", "Times New Roman", Georgia, serif; + --sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif; - --pad: clamp(16px, 3vw, 32px); - --gap-section: clamp(72px, 7.5vw, 112px); + --pad: clamp(16px, 3.2vw, 40px); + --max: 1180px; + --gap: clamp(24px, 3vw, 44px); - --font: "General Sans", ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif; - --ease: cubic-bezier(0.2, 0.8, 0.2, 1); + --step-xs: 11px; + --step-sm: 13px; + --step-body: 16px; + --step-lead: clamp(17px, 1.1vw, 19px); + --step-h3: clamp(19px, 1.5vw, 23px); + --step-h2: clamp(30px, 3.4vw, 46px); + --step-display: clamp(40px, 6.4vw, 92px); } *, *::before, *::after { box-sizing: border-box; } -html { - -webkit-text-size-adjust: 100%; - scroll-behavior: smooth; -} +html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; } body { margin: 0; - background: var(--bg); + background: var(--paper); color: var(--ink); - font-family: var(--font); - font-size: var(--t-body); - line-height: 1.45; - font-weight: 400; - overflow-x: hidden; + font-family: var(--sans); + font-size: var(--step-body); + line-height: 1.55; } img { display: block; max-width: 100%; height: auto; } a { color: inherit; } button, input { font: inherit; color: inherit; } -:focus-visible { - outline: 2px solid var(--ink); - outline-offset: 4px; -} -::selection { background: var(--ink); color: var(--bg); } +:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; } +::selection { background: var(--ink); color: var(--paper); } .sr-only { - position: absolute; width: 1px; height: 1px; - padding: 0; margin: -1px; overflow: hidden; - clip: rect(0 0 0 0); white-space: nowrap; border: 0; + position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; + overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; } .skip { position: fixed; top: 8px; left: 8px; z-index: 60; - padding: 10px 14px; background: var(--ink); color: var(--bg); - font-size: var(--t-small); text-decoration: none; - transform: translateY(-160%); - transition: transform 240ms var(--ease); + padding: 10px 16px; background: var(--ink); color: var(--paper); + font-size: var(--step-sm); text-decoration: none; + transform: translateY(-160%); transition: transform 220ms ease; } .skip:focus { transform: translateY(0); } -/* ---------- top bar: wordmark left, links spread right ---------- */ +/* ---------- masthead ---------- */ -.topbar { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 24px; - padding: 24px var(--pad) 0; +.masthead { + max-width: var(--max); + margin: 0 auto; + padding: clamp(20px, 3vw, 34px) var(--pad) 18px; + display: grid; + grid-template-columns: 1fr auto; + align-items: end; + gap: 14px; + border-bottom: 1px solid var(--rule); } -.mark { - font-size: var(--t-small); - font-weight: 500; +.wordmark { + font-family: var(--serif); + font-weight: 700; + font-size: clamp(40px, 6.2vw, 76px); + letter-spacing: -0.035em; + line-height: 0.86; + text-transform: uppercase; text-decoration: none; - letter-spacing: -0.01em; } .nav { display: flex; flex-wrap: wrap; - gap: clamp(12px, 2.4vw, 40px); - font-size: var(--t-small); + align-items: center; + gap: clamp(12px, 1.8vw, 26px); + font-size: var(--step-xs); + font-weight: 500; + letter-spacing: 0.16em; + text-transform: uppercase; } -.nav a { text-decoration: none; color: var(--muted); } -.nav a:hover, .nav a:focus-visible { color: var(--ink); } -.nav .nav-action { color: var(--ink); } +.nav a { text-decoration: none; padding: 6px 0; } +.nav a:hover { text-decoration: underline; text-underline-offset: 5px; } -/* ---------- hero ---------- */ - -.hero { - min-height: 92svh; - display: flex; - flex-direction: column; - justify-content: center; - padding: clamp(56px, 9vh, 110px) var(--pad) 32px; +.nav-cta { + grid-column: 2; + justify-self: end; + margin-top: 10px; + padding: 9px 18px; + border: 1px solid var(--ink); + font-size: var(--step-xs); + font-weight: 600; + letter-spacing: 0.16em; + text-transform: uppercase; + text-decoration: none; + transition: background-color 160ms ease, color 160ms ease; } +.nav-cta:hover, .nav-cta:focus-visible { background: var(--ink); color: var(--paper); } -.hero-part { white-space: nowrap; } +/* ---------- lead ---------- */ -.hero-line { - margin: 0; - display: flex; - flex-wrap: wrap; - align-items: baseline; - gap: 0 0.2em; - font-size: var(--t-display); - font-weight: 700; - letter-spacing: -0.035em; - line-height: 0.94; -} - -/* the photograph set into the sentence, one line tall */ -.slot { - display: inline-block; - width: 2.8em; - height: 0.76em; - vertical-align: 0; - overflow: hidden; - background: var(--surface); -} -.slot img { - width: 100%; height: 100%; - object-fit: cover; - object-position: 50% 52%; -} - -.hero-foot { - margin-top: clamp(40px, 7vh, 88px); +.lead { + max-width: var(--max); + margin: 0 auto; + padding: var(--gap) var(--pad) clamp(36px, 5vw, 64px); display: grid; - grid-template-columns: repeat(12, 1fr); - gap: var(--pad); - align-items: end; + grid-template-columns: 3fr 7fr; + grid-template-areas: + "side media" + "side caption"; + column-gap: var(--gap); + row-gap: 22px; } -.hero-say { - grid-column: 5 / 11; - margin: 0; - max-width: 34em; - font-size: var(--t-body); - line-height: 1.5; -} -.hero-foot .btn { grid-column: 11 / 13; justify-self: end; } -/* ---------- buttons: one shape, square, filled ---------- */ +.lead-side { grid-area: side; display: flex; flex-direction: column; gap: clamp(22px, 3vw, 36px); } + +.box { + border: 1px solid var(--rule-strong); + padding: 22px 20px 20px; +} +.box-kicker { + margin: 0 0 12px; + font-size: var(--step-xs); + font-weight: 600; + letter-spacing: 0.16em; + text-transform: uppercase; +} +.box-lede { + margin: 0 0 18px; + font-family: var(--serif); + font-style: italic; + font-size: var(--step-lead); + line-height: 1.45; +} .btn { display: inline-flex; align-items: center; justify-content: center; - min-height: 48px; - padding: 0 26px; - background: var(--accent); - color: var(--accent-ink); - font-size: var(--t-small); - font-weight: 500; + min-height: 44px; + padding: 0 22px; + background: var(--ink); + color: var(--paper); + border: 1px solid var(--ink); + border-radius: 0; + font-size: var(--step-xs); + font-weight: 600; + letter-spacing: 0.16em; + text-transform: uppercase; text-decoration: none; - border: 0; - border-radius: 0; cursor: pointer; - transition: transform 200ms var(--ease), background-color 200ms var(--ease); + transition: background-color 160ms ease, color 160ms ease; } -.btn:hover { background: var(--ink); transform: translateY(-2px); } -.btn:active { transform: translateY(0); } +.btn:hover, .btn:focus-visible { background: transparent; color: var(--ink); } -/* ---------- intro: statement with inline thumbnails ---------- */ - -.intro { - padding: clamp(56px, 6vw, 96px) var(--pad) clamp(80px, 9vw, 144px); - display: flex; - justify-content: center; -} - -.statement { - margin: 0; - max-width: 21em; - font-size: var(--t-statement); - line-height: 1.18; - letter-spacing: -0.015em; - font-weight: 500; -} - -.thumb { - display: inline-block; - width: 1.7em; - height: 1em; - margin: 0 0.12em; - vertical-align: -0.1em; - overflow: hidden; - border-radius: 0.06em; - background: var(--surface); -} -.thumb img { width: 100%; height: 100%; object-fit: cover; } -.thumb img { object-position: 50% 40%; } - -/* ---------- open: map to full width ---------- */ - -.open { padding-bottom: var(--gap-section); overflow: clip; } - -.map-figure { margin: 0; overflow: clip; } - -.map-figure img { - width: 100%; - max-width: 100%; - height: clamp(220px, 30vw, 420px); - object-fit: cover; - object-position: 50% 45%; - background: var(--surface); -} - -.map-figure figcaption { - padding: 14px var(--pad) 0; - font-size: var(--t-small); - color: var(--muted); - max-width: 52em; -} - -.open-copy { - margin-top: clamp(48px, 6vw, 88px); - padding: 0 var(--pad); - display: grid; - grid-template-columns: repeat(12, 1fr); - gap: var(--pad); -} -.open-copy h2 { - grid-column: 1 / 7; - margin: 0; - font-size: var(--t-head); - font-weight: 700; - letter-spacing: -0.03em; - line-height: 0.98; -} -.open-copy p { - grid-column: 8 / 13; - margin: 0; - max-width: 30em; - line-height: 1.55; -} - -.open-figure { - grid-column: 10 / 13; - margin: clamp(32px, 4vw, 56px) 0 0; -} -.open-figure img { - width: 100%; - aspect-ratio: 4 / 5; - object-fit: cover; - object-position: 50% 38%; - background: var(--surface); -} - -/* ---------- claims: sentences with switches ---------- */ - -.claims { - background: var(--surface); - padding: clamp(24px, 3vw, 40px) 0 calc(var(--gap-section) * 0.6); -} - -.band { - border: 0; - background: transparent; - transition: background-color 400ms var(--ease); - padding: 0 var(--pad); -} -.band[data-on="true"] { background: var(--bg); } - -.band-switch { - display: grid; - grid-template-columns: 1fr auto; - align-items: center; - gap: 24px; - width: 100%; - min-height: clamp(112px, 17vh, 168px); - padding: 18px 0; - background: none; - border: 0; - text-align: left; - cursor: pointer; -} - -.band-text { - font-size: var(--t-statement); - font-weight: 500; - letter-spacing: -0.03em; - line-height: 1; - color: var(--muted); - transition: color 300ms var(--ease); -} -.band[data-on="true"] .band-text { color: var(--ink); } - -.track { - position: relative; - display: block; - width: 2.4em; - height: 1.15em; - min-width: 52px; - min-height: 26px; - background: var(--bg); - transition: background-color 300ms var(--ease); -} -.band[data-on="true"] .track { background: var(--ink); } - -.knob { - position: absolute; - top: 0.14em; - left: 0.14em; - width: calc(1.15em - 0.28em); - height: calc(1.15em - 0.28em); - min-width: 18px; - min-height: 18px; - background: var(--muted); - transition: transform 300ms var(--ease), background-color 300ms var(--ease); -} -.band[data-on="true"] .knob { - transform: translateX(1.25em); - background: var(--bg); -} - -.band-value { - margin: 0; - max-width: 46em; - padding: 0 0 22px; - font-size: var(--t-body); - line-height: 1.5; - color: var(--muted); - opacity: 0.6; - transition: opacity 300ms var(--ease), color 300ms var(--ease); -} -.band[data-on="true"] .band-value { opacity: 1; color: var(--ink); } - -.band-value code { - font-family: var(--font); - font-weight: 500; - color: var(--ink); -} - -/* ---------- tips: statement left, table pushed right ---------- */ - -.tips { padding: var(--gap-section) 0; } - -.tips h2 { - margin: 0 0 96px; - padding-left: var(--pad); - padding-right: var(--pad); - font-size: var(--t-head); - font-weight: 700; - letter-spacing: -0.035em; - line-height: 0.96; - max-width: 15em; -} -.tips h2::first-line { padding-left: 3vw; } - -.tips-grid { - display: grid; - grid-template-columns: repeat(12, 1fr); - gap: var(--pad); - padding: 0 var(--pad); -} - -.tips-side { grid-column: 1 / 6; } - -.tips-note { - margin: 0 0 40px; - max-width: 24em; - color: var(--muted); - line-height: 1.55; -} - -/* the live thing */ -.picker { - background: var(--surface); - padding: clamp(18px, 2vw, 28px); - max-width: 30em; -} -.picker-q { - margin: 0 0 16px; - font-size: var(--t-small); - color: var(--muted); -} -.chips { - display: flex; - flex-wrap: wrap; - gap: 8px; -} -.chip { - padding: 10px 14px; - min-height: 40px; - background: var(--bg); - color: var(--ink); - border: 0; - border-radius: 0; - font-size: var(--t-small); - cursor: pointer; - transition: background-color 180ms var(--ease), color 180ms var(--ease), transform 180ms var(--ease); -} -.chip:hover { transform: translateY(-1px); } -.chip[aria-pressed="true"] { background: var(--ink); color: var(--bg); } - -.picker-out { - margin: 20px 0 0; - font-size: var(--t-body); - line-height: 1.4; - min-height: 2.8em; - color: var(--ink); -} -.picker-out .mod { font-weight: 700; } -.picker-out .arrow { color: var(--muted); } - -.tips-figure { - margin: clamp(28px, 4vw, 48px) 0 0; - width: min(100%, 22em); -} -.tips-figure img { - width: 100%; - aspect-ratio: 4 / 5; - object-fit: cover; - object-position: 50% 42%; - background: var(--surface); -} - -.tips-list { - grid-column: 8 / 13; +.side-list { margin: 0; padding: 0; list-style: none; + border-top: 1px solid var(--rule); + font-size: var(--step-sm); + line-height: 1.5; } -.tips-list li { - display: grid; - grid-template-columns: 7.5em 1fr; - gap: 4px 16px; - padding-bottom: 26px; - font-size: var(--t-body); -} -.tips-list li:nth-child(4) { padding-top: 18px; } - -.tip-kind { color: var(--muted); font-size: var(--t-small); padding-top: 3px; } -.tip-what { line-height: 1.35; } -.tip-where { - grid-column: 2; +.side-list li { padding: 12px 0; border-bottom: 1px solid var(--rule); } +.side-kicker { + display: block; + font-size: var(--step-xs); + letter-spacing: 0.16em; + text-transform: uppercase; color: var(--muted); - font-size: var(--t-small); - text-decoration: none; + margin-bottom: 3px; } -a.tip-where { color: var(--ink); text-underline-offset: 3px; text-decoration: underline; } -a.tip-where:hover { color: var(--bg); background: var(--ink); } +.side-list a { text-decoration: underline; text-underline-offset: 3px; } -/* ---------- about: portrait as drop cap ---------- */ +.lead-media { grid-area: media; } +.lead-media img { width: 100%; aspect-ratio: 16 / 11; object-fit: cover; object-position: 50% 42%; } -.about { - padding: var(--gap-section) var(--pad); +.lead-caption { grid-area: caption; max-width: 36em; } + +.meta { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: 14px; + margin: 0 0 12px; + font-size: var(--step-xs); + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--muted); +} +.meta span:first-child { color: var(--ink); font-weight: 600; } + +h1 { + margin: 0 0 12px; + font-family: var(--serif); + font-weight: 500; + font-size: clamp(26px, 3.1vw, 42px); + line-height: 1.1; + letter-spacing: -0.015em; +} + +.standfirst { + margin: 0; + font-family: var(--serif); + font-size: var(--step-lead); + line-height: 1.55; + color: #2a2926; +} + +/* ---------- section frame ---------- */ + +.section { + max-width: var(--max); + margin: 0 auto; + padding: clamp(34px, 4.5vw, 56px) var(--pad) 0; +} + +.section-head { display: grid; - grid-template-columns: repeat(12, 1fr); - grid-template-areas: - "head head head head head head head head head head head head" - "bio bio bio bio bio bio bio bio bio . . ." - "facts facts facts facts . . . . . . . ."; - gap: 0 var(--pad); + grid-template-columns: auto 1fr auto; + align-items: baseline; + column-gap: 16px; + padding-bottom: 14px; + border-bottom: 1px solid var(--ink); + margin-bottom: clamp(22px, 3vw, 36px); } -.about-facts { - grid-area: facts; - margin: 40px 0 0; - max-width: 32em; + +.initial { + font-family: var(--serif); + font-size: clamp(54px, 7.4vw, 96px); + line-height: 0.7; + font-weight: 400; +} + +.section-title { + margin: 0; + font-family: var(--sans); + font-size: var(--step-xs); + font-weight: 600; + letter-spacing: 0.18em; + text-transform: uppercase; +} +.section-title em { + font-family: var(--serif); + font-style: italic; + font-size: var(--step-h3); + font-weight: 400; + letter-spacing: 0; + text-transform: none; + margin-left: 8px; +} + +.more { + font-size: var(--step-xs); + font-weight: 600; + letter-spacing: 0.16em; + text-transform: uppercase; + text-decoration: none; + border-bottom: 1px solid var(--ink); + padding-bottom: 2px; + justify-self: end; +} +.more:hover, .more:focus-visible { background: var(--ink); color: var(--paper); } + +/* ---------- cards ---------- */ + +.cards { + display: grid; + grid-template-columns: repeat(3, 1fr); + border-top: 1px solid var(--rule); +} + +.card { display: flex; flex-direction: column; gap: 10px; - font-size: var(--t-small); - color: var(--muted); + padding: 20px clamp(12px, 1.8vw, 24px) 22px; + text-decoration: none; + border-bottom: 1px solid var(--rule); + transition: background-color 200ms ease; } -.about-facts a { color: var(--ink); } +.card + .card { border-left: 1px solid var(--rule); } +.card:hover, .card:focus-visible { background: var(--tint); } -.about h2 { - grid-area: head; - margin: 0 0 28px; - font-size: var(--t-head); - font-weight: 700; - letter-spacing: -0.03em; - line-height: 1; -} - -.bio { - grid-area: bio; - margin: 0; - max-width: 26em; - font-size: var(--t-statement); - line-height: 1.22; - letter-spacing: -0.01em; - font-weight: 400; -} -.dropcap { - float: left; - width: 5em; - height: 5em; - margin: 0.1em 0.5em 0.2em 0; +.card-img { + width: 100%; + aspect-ratio: 4 / 3; object-fit: cover; - object-position: 30% 40%; - background: var(--surface); + object-position: 50% 45%; + background: var(--tint); } -/* ---------- ask: a sentence with a blank ---------- */ - -.ask { - background: var(--surface); - padding: var(--gap-section) var(--pad); - max-width: 100%; +.card-cat { + display: block; + padding: 7px 0; + border-top: 1px solid var(--rule); + border-bottom: 1px solid var(--rule); + text-align: center; + font-size: var(--step-xs); + font-weight: 600; + letter-spacing: 0.16em; + text-transform: uppercase; } -.ask-line { + +.card h3 { + margin: 2px 0 0; + font-family: var(--serif); + font-weight: 500; + font-size: var(--step-h3); + line-height: 1.25; + text-align: center; +} + +.card-desc { margin: 0; - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0.22em; - font-size: var(--t-head); - font-weight: 700; - letter-spacing: -0.03em; - line-height: 1.1; -} -.ask-line label { flex: 0 0 auto; } -.ask-line input { - flex: 1 1 7em; - min-width: 6em; - height: 1.2em; - padding: 0 0.2em; - background: var(--surface); - border: 0; - border-radius: 0; - font-size: 0.5em; - font-weight: 500; - letter-spacing: 0; -} -.ask-line input::placeholder { color: var(--muted); } -.ask-send { - flex: 0 0 auto; - height: 1.05em; - padding: 0 0.4em; - background: var(--accent); - color: var(--accent-ink); - border: 0; - border-radius: 0; - font-size: 0.5em; - font-weight: 500; - letter-spacing: 0; - cursor: pointer; - transition: background-color 180ms var(--ease), transform 180ms var(--ease); -} -.ask-send:hover { background: var(--ink); transform: translateY(-1px); } - -.ask-note { - margin: 28px 0 0; - font-size: var(--t-small); + font-size: var(--step-sm); + line-height: 1.5; color: var(--muted); - max-width: 40em; + text-align: center; } -/* ---------- footer: invitation over a cropped name ---------- */ - -.foot { - background: var(--ink); - color: var(--bg); - padding: clamp(56px, 7vw, 96px) var(--pad) 0; - overflow: hidden; - min-height: 88svh; - display: flex; - flex-direction: column; +.card-meta { + margin-top: auto; + padding-top: 6px; + font-family: var(--serif); + font-style: italic; + font-size: var(--step-sm); + color: var(--muted); + text-align: center; } -.foot-top { +.card:hover h3, .card:focus-visible h3 { text-decoration: underline; text-underline-offset: 4px; } + +/* ---------- featured module ---------- */ + +.feature { display: grid; - grid-template-columns: repeat(12, 1fr); - gap: var(--pad); - align-items: start; + grid-template-columns: 4fr 7fr; + border: 1px solid var(--ink); } -.foot-invite { - grid-column: 1 / 8; + +.feature-plate { + padding: clamp(20px, 2.4vw, 30px); + border-right: 1px solid var(--ink); + background: var(--tint); +} + +.plate-kicker { + margin: 0 0 18px; + font-size: var(--step-xs); + font-weight: 600; + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--muted); +} + +.plate-name { margin: 0; - font-size: var(--t-head); - font-weight: 700; - letter-spacing: -0.035em; - line-height: 0.98; - max-width: 12em; + font-family: var(--serif); + font-size: clamp(34px, 4vw, 56px); + line-height: 0.95; + letter-spacing: -0.02em; } -.foot-top .btn { - grid-column: 10 / 13; - justify-self: end; - background: var(--bg); + +.plate-facts { + margin: 22px 0 0; + padding: 0; + list-style: none; + border-top: 1px solid var(--rule-strong); + font-size: var(--step-sm); +} +.plate-facts li { + display: flex; + justify-content: space-between; + gap: 12px; + padding: 9px 0; + border-bottom: 1px solid var(--rule); +} +.plate-facts span { + font-size: var(--step-xs); + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--muted); +} + +.plate-note { + margin: 18px 0 0; + font-family: var(--serif); + font-style: italic; + font-size: var(--step-sm); + line-height: 1.45; + color: var(--muted); +} + +.feature-body { padding: clamp(22px, 2.8vw, 36px); } + +.feature-name { + margin: 0 0 12px; + font-family: var(--serif); + font-weight: 500; + font-size: clamp(24px, 2.6vw, 34px); + line-height: 1.15; +} + +.feature-body > p { margin: 0 0 20px; max-width: 40em; } + +.benefits { + margin: 0 0 24px; + padding: 0; + list-style: none; + border-top: 1px solid var(--rule); +} +.benefits li { + padding: 12px 0; + border-bottom: 1px solid var(--rule); + font-size: var(--step-sm); + line-height: 1.5; + color: var(--muted); +} +.benefits strong { + display: block; + margin-bottom: 2px; + font-family: var(--serif); + font-size: var(--step-body); + font-weight: 500; color: var(--ink); } -.foot-top .btn:hover { background: var(--surface); color: var(--ink); } -.foot-links { - margin-top: clamp(40px, 6vw, 80px); - display: flex; - flex-wrap: wrap; - gap: 24px; - font-size: var(--t-body); +.feature-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; } +.feature-note { + font-family: var(--serif); + font-style: italic; + font-size: var(--step-sm); + color: var(--muted); } -.foot-links a { text-decoration: underline; text-underline-offset: 4px; } -.foot-links a:hover { text-decoration-thickness: 2px; } -.foot-credits { - margin: 24px 0 0; - font-size: var(--t-small); - line-height: 1.5; - max-width: 62em; - opacity: 0.9; +/* ---------- community news ---------- */ + +.news { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--rule); } + +.news li { + display: grid; + grid-template-columns: 120px 1fr 190px; + gap: 20px; + padding: 20px 0; + border-bottom: 1px solid var(--rule); +} + +.news-date { + font-family: var(--serif); + font-style: italic; + font-size: var(--step-sm); + color: var(--muted); + padding-top: 4px; +} + +.news-body h3 { + margin: 0 0 6px; + font-family: var(--serif); + font-weight: 500; + font-size: var(--step-h3); + line-height: 1.2; +} +.news-body h3 a { text-decoration: none; } +.news-body h3 a:hover { text-decoration: underline; text-underline-offset: 4px; } +.news-body p { margin: 0; font-size: var(--step-sm); line-height: 1.5; color: var(--muted); max-width: 52em; } + +.news-source { + font-size: var(--step-xs); + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); + text-align: right; + padding-top: 6px; + overflow-wrap: anywhere; +} + +/* ---------- ask ---------- */ + +.ask { + max-width: var(--max); + margin: clamp(40px, 6vw, 72px) auto 0; + padding: 0 var(--pad); +} +.ask-inner { + border-top: 1px solid var(--ink); + border-bottom: 1px solid var(--ink); + padding: clamp(26px, 3vw, 40px) 0; + display: grid; + grid-template-columns: 3fr 7fr; + gap: var(--gap); + align-items: start; +} +.ask h2 { + margin: 0; + font-family: var(--serif); + font-weight: 500; + font-size: var(--step-h2); + line-height: 1.05; +} +.ask-lede { margin: 8px 0 0; font-family: var(--serif); font-style: italic; color: var(--muted); } + +.ask-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; } +.ask-form label { + flex: 0 0 100%; + font-size: var(--step-xs); + letter-spacing: 0.16em; + text-transform: uppercase; + color: var(--muted); +} +.ask-form input { + flex: 1 1 240px; + min-height: 44px; + padding: 0 14px; + background: transparent; + border: 1px solid var(--rule-strong); + border-radius: 0; + font-size: var(--step-body); +} +.ask-form input:focus-visible { border-color: var(--ink); } + +/* ---------- footer ---------- */ + +.foot { + margin-top: clamp(48px, 7vw, 88px); + background: var(--ink); + color: #f4f2ee; + padding: clamp(32px, 4vw, 52px) var(--pad) 24px; } -.foot-credits a { text-underline-offset: 2px; } .foot-name { - margin: auto 0 0; - font-size: clamp(64px, 20vw, 280px); + max-width: var(--max); + margin: 0 auto 26px; + font-family: var(--serif); font-weight: 700; - letter-spacing: -0.05em; - line-height: 0.8; - white-space: nowrap; - transform: translateY(22%); - user-select: none; + font-size: clamp(44px, 9vw, 116px); + line-height: 0.86; + letter-spacing: -0.035em; + text-transform: uppercase; } +.foot-cols { + max-width: var(--max); + margin: 0 auto; + display: grid; + grid-template-columns: 1fr auto; + gap: 24px; + padding-top: 20px; + border-top: 1px solid rgba(255, 255, 255, 0.25); +} + +.foot-note { margin: 0; max-width: 34em; font-family: var(--serif); font-style: italic; font-size: var(--step-sm); line-height: 1.5; } + +.foot-links { display: flex; flex-wrap: wrap; gap: 18px; font-size: var(--step-xs); letter-spacing: 0.16em; text-transform: uppercase; } +.foot-links a { text-decoration: none; border-bottom: 1px solid rgba(255, 255, 255, 0.4); padding-bottom: 2px; } +.foot-links a:hover { border-color: #fff; } + +.foot-credits { + max-width: var(--max); + margin: 22px auto 0; + font-size: var(--step-xs); + line-height: 1.6; + color: rgba(255, 255, 255, 0.62); +} +.foot-credits a { color: rgba(255, 255, 255, 0.82); } + /* ---------- motion ---------- */ html.js [data-rv] { opacity: 0; - transform: translateY(22px); - transition: opacity 620ms var(--ease), transform 620ms var(--ease); + transform: translateY(14px); + transition: opacity 520ms ease, transform 520ms ease; } html.js [data-rv].rv-in { opacity: 1; transform: none; } -html.js [data-rv="2"] { transition-delay: 70ms; } -html.js [data-rv="3"] { transition-delay: 140ms; } -html.js [data-rv="4"] { transition-delay: 210ms; } - -/* signature move: the shutter on the hero photograph */ -html.js .slot { clip-path: inset(0 100% 0 0); } -html.js .hero-line.shutter .slot { - clip-path: inset(0 0 0 0); - transition: clip-path 1100ms cubic-bezier(0.16, 1, 0.3, 1); -} -html.js .slot img { transform: scale(1.12); transition: transform 1600ms cubic-bezier(0.16, 1, 0.3, 1); } -html.js .hero-line.shutter .slot img { transform: scale(1); } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } html.js [data-rv] { opacity: 1; transform: none; transition: none; } - html.js .slot, html.js .hero-line.shutter .slot { clip-path: none; transition: none; } - html.js .slot img, html.js .hero-line.shutter .slot img { transform: none; transition: none; } - .map-figure img { transform: none !important; } } -/* ---------- narrow screens ---------- */ +/* ---------- tablet ---------- */ @media (max-width: 900px) { - .hero-foot { grid-template-columns: 1fr; gap: 24px; } - .hero-say { grid-column: 1 / -1; } - .hero-foot .btn { grid-column: 1 / -1; justify-self: start; } + .lead { + grid-template-columns: 1fr; + grid-template-areas: "media" "caption" "side"; + } + .lead-side { flex-direction: row; flex-wrap: wrap; } + .box { flex: 1 1 240px; } + .side-list { flex: 1 1 260px; } - .open-copy h2, - .open-copy p, - .open-figure { grid-column: 1 / -1; } - .open-figure { width: min(100%, 24em); } + .feature { grid-template-columns: 1fr; } + .feature-plate { border-right: 0; border-bottom: 1px solid var(--ink); } - .tips-side, - .tips-list { grid-column: 1 / -1; } - .tips-note { margin-bottom: 28px; } + .news li { grid-template-columns: 96px 1fr; } + .news-source { grid-column: 2; text-align: left; padding-top: 0; } - .about h2, .bio, .about-facts { grid-column: 1 / -1; } - - .foot-invite { grid-column: 1 / -1; } - .foot-top .btn { grid-column: 1 / -1; justify-self: start; margin-top: 28px; } - .foot { min-height: auto; } + .ask-inner { grid-template-columns: 1fr; } } -@media (max-width: 700px) { - .hero-line { - flex-direction: column; - align-items: flex-start; - gap: 14px; - font-size: clamp(26px, 7.6vw, 34px); - line-height: 1.04; - } - .slot { - width: 1.7em; - height: 0.7em; - vertical-align: 0; - } +/* ---------- phone ---------- */ - .about { - grid-template-areas: - "head head head head head head head head head head head head" - "bio bio bio bio bio bio bio bio bio bio bio bio" - "facts facts facts facts facts facts facts facts facts facts facts facts"; - gap: 0; - } - .about-facts { flex-direction: row; flex-wrap: wrap; gap: 6px 18px; margin: 28px 0 0; } +@media (max-width: 680px) { + .masthead { grid-template-columns: 1fr; align-items: start; gap: 12px; } + .nav { gap: 14px; } + .nav-cta { grid-column: 1; justify-self: start; margin-top: 0; } + + .lead-side { flex-direction: column; } + + .cards { grid-template-columns: 1fr; } + .card { padding: 18px 0 22px; } + .card + .card { border-left: 0; } + .card-img { aspect-ratio: 16 / 9; } + + .section-head { grid-template-columns: auto 1fr; row-gap: 10px; } + .more { grid-column: 1 / -1; justify-self: start; } + + .news li { grid-template-columns: 1fr; gap: 8px; } + .news-date, .news-source { padding-top: 0; } + + .ask-form input { flex: 1 1 100%; } + .ask-form .btn { width: 100%; } + + .foot-cols { grid-template-columns: 1fr; } } -@media (max-width: 620px) { - .topbar { padding-top: 18px; } - .nav { gap: 14px; font-size: 13px; } - .nav a:not(.nav-action):not(:first-child) { display: none; } - - .hero { padding-top: 96px; } - .hero-line { line-height: 0.98; } - .slot { width: 1.5em; height: 0.6em; } - - .statement { font-size: 22px; line-height: 1.25; } - .statement .thumb:nth-of-type(2) { display: none; } - - .band-switch { min-height: 132px; gap: 16px; } - .band-text { line-height: 1.06; } - - .tips h2 { margin-bottom: 56px; } - .tips-list li { grid-template-columns: 1fr auto; padding-bottom: 22px; } - .tip-kind { grid-column: 1; } - .tip-what { grid-column: 1 / -1; } - .tip-where { grid-column: 1 / -1; } - - .dropcap { width: 3.2em; height: 3.2em; } - .bio { font-size: 22px; line-height: 1.28; } - - .ask-line { font-size: var(--t-statement); } - .ask-line label { flex: 1 1 100%; } - .ask-line input { flex: 1 1 100%; order: 3; height: 2.4em; font-size: 0.8em; } - .ask-send { order: 4; height: 2em; font-size: 0.8em; } - - .foot-name { font-size: 26vw; } +/* ---------- touch targets on phones ---------- */ +@media (max-width: 680px) { + .nav a, + .more, + .foot-links a, + .side-list a, + .news-body h3 a { + display: inline-flex; + align-items: center; + min-height: 44px; + } + .nav-cta { min-height: 44px; } + .foot-links { gap: 10px 18px; } } diff --git a/index.html b/index.html index 5050fb1..ce565ee 100644 --- a/index.html +++ b/index.html @@ -3,232 +3,200 @@ -The OC Guy — Odoo Community tips and tricks - - - - - +The OC Guy — Odoo Community tips, modules and community news + + + + + -
- The OC Guy +
+ The OC Guy + Ask a question
- -
-

- Odoo Community - A 1689 engraved world map in two hemispheres, ringed by allegorical figures and ships - tips and tricks. -

-
-

Hey — welcome. Here you find the best tips and tricks for Odoo Community, - the open-source ERP small companies run themselves: install it, back it up, extend it, and - bolt AI on top. I am The OC Guy, Oliver Arnold.

- Read the tips +
+
+
+

Get the most out of Odoo Community

+

Install it, back it up, extend it, bolt AI on top — the notes I wish I had on day one.

+ Read the tips +
+
    +
  • Written by Oliver Arnold, 安雷威
  • +
  • Since 2024, in German, English and Spanish
  • +
  • Contact o.arnold@projektbox.de
  • +
-
- -
-

What Odoo Community is

-

- A full ERP whose licence costs nothing: - An engraving of four kinds of globe, opened to show their rings and axes - read every line, change it, run it on your own server - An engraved seventeenth-century printer's workshop: a hand press, workmen setting type - and it answers to you. -

-
- - -
-
- A 1794 wall map of the world in two hemispheres, engraved with notes on voyages and discoveries -
A 1794 double-hemisphere map: knowledge printed once, copied by hand, corrected by everyone who used it.
+
+ A 1794 wall map of the world engraved in two hemispheres, with marginal notes on voyages and discoveries
-
-

Written in the open, used in the open

-

Odoo Community is published under LGPL v3. No seat count, no trial that expires, no module you must - rent back. The people who make it stronger are the people who run it: localisation packs for your - country's taxes, modules for your industry, a patch for the bug you hit last week. That is the deal — - you inherit a working ERP, and you owe the next person what you learned.

-
- The 1689 world map again, closer: the engraved landmasses and sea routes of two hemispheres -
+ +
+

GuideOdoo Community

+

Welcome. Best tips and tricks for Odoo Community.

+

Hey — you are in the right place. This is where I write down how to run the open-source + ERP that small companies own themselves: the install that holds, the backup that restores, the module + that already exists, and the AI that sits on top of your records instead of guessing at them.

- -
-

What you get with Odoo Community

+
+
+ +

The latest in Tips & Tricks

+ View all tips +
-
- -

Zero per user, per month. Odoo Community is LGPL v3: you pay for a server and your own time, not for permission.

-
+
+ + A 1689 engraved world map in two hemispheres + Install +

Run Odoo from Docker Compose, not from source

+

Two containers, one file, a volume you can copy. The shortest path to a server you + understand and can move next year without a migration project.

+ 6 min read +
-
- -

PostgreSQL on your own machine. One pg_dump is the whole company, portable to any host you like.

-
+ + An engraved seventeenth-century printer's workshop with a hand press and workmen setting type + Backup +

Dump the database nightly and restore it monthly

+

A backup you have never restored is a hope, not a backup. One pg_dump, one cron line, + and a restore into a scratch copy that takes four minutes.

+ 4 min read +
-
- -

The source is public. Find the field, change the field, keep the change — no vendor to convince, no feature request to queue.

-
- -
- -

The OCA repositories and the Odoo app store carry thousands of modules, most of them free, written by people solving their own problem first.

-
- -
- -

The ERP keeps the truth — stock, invoices, orders. The model reads it and drafts with it. Boring, correct data in; useful answers out.

+ + An engraving of four kinds of globe on stands + Extend +

Take an OCA module before you write your own

+

Someone already solved your problem and maintains it for every series. Reading their + code teaches you the ORM faster than any tutorial.

+ 8 min read +
- -
-

Tips that save a weekend

-
-
-

Seven things worth knowing before your first install, and before every upgrade after it.

+
+
+ +

Featured Module of the month

+
- -
-

What does your company actually do?

-
- - - - - - -
-

Choose one, or several. The modules stack in the order you would install them.

-
- -
- An engraving of four kinds of globe on stands, opened to show their rings and axes -
+
+
+

Odoo Community · LGPL-3

+

Inventory

+
    +
  • Series 18.0
  • +
  • Depends on Product, UoM
  • +
  • Install Apps → Inventory
  • +
+

Module plate: the facts above come from the module manifest, not from a vendor page.

-
    -
  1. - Install - Run it from Docker Compose, not from source. - One file, two containers -
  2. -
  3. - Backup - Dump the database nightly and actually restore it monthly. - pg_dump, then psql -
  4. -
  5. - Upgrade - Stay on one major series, and migrate a copy before the live one. - Never in place -
  6. -
  7. - Extend - Take an OCA module before you write your own. - github.com/OCA -
  8. -
  9. - Extend - Keep your code in one addons path and never edit core files. - Inherit, do not patch -
  10. -
  11. - Debug - Answer "where does this field live" with the Odoo shell, not with grep. - odoo shell -
  12. -
  13. - Invoice - Load the localisation pack for your country before the first invoice. - l10n_<your country> -
  14. -
+
+

Inventory, before you buy anything

+

One module holds what you own, where it sits and what it is worth: stock moves, reordering rules, + lots and serial numbers, three warehouse steps if you need them, one if you do not.

+
    +
  • Real stock, not a spreadsheetEvery receipt, delivery and adjustment is a move with a date and a person behind it.
  • +
  • Barcode-ready on day oneThe Community module ships the operations your scanner needs; no paid add-on.
  • +
  • Multi-warehouse without a second systemLocations, routes and reordering rules live in the same database as your invoices.
  • +
+
+ Explore module + Free, in the Community edition +
+
- -
-

Who is typing

-

- An engraved printer's workshop: a hand press on the left, workmen setting type in the centre - I am Oliver. I run Odoo Community for my own company and write down what breaks. - No partner, no licences: own your ERP, bolt AI on. -

-

- Oliver Arnold, 安雷威 - o.arnold@projektbox.de -

+
+
+ +

The latest in Community news

+ View all community news +
+ +
    +
  • + +
    +

    RMA: returns, repairs and replacements inside Odoo

    +

    Return Merchandise Authorization for the whole flow — the customer sends it back, you decide repair + or credit, the stock move happens once.

    +
    + github.com/OCA/rma +
  • +
  • + +
    +

    Helpdesk modules that stay out of your way

    +

    Tickets, teams and SLAs without the enterprise edition: the OCA helpdesk repository keeps a branch + per supported series, so an upgrade does not orphan your support desk.

    +
    + github.com/OCA/helpdesk +
  • +
  • + +
    +

    Localisation is community work: Italy's charts, kept current

    +

    Tax reports, e-invoicing and the country chart of accounts, maintained by the people who file with + them. The same pattern exists for your country.

    +
    + github.com/OCA/l10n-italy +
  • +
-
-

Ask a question

-

- - - -

-

One question, one answer, by mail. No list, no tracking, no sequence of follow-ups.

+
+

Ask a question

+

One question, one answer, by mail. No list, no tracking, no follow-up sequence.

+
+ + + +
+
- diff --git a/js/site.js b/js/site.js index 547f699..f01aec8 100644 --- a/js/site.js +++ b/js/site.js @@ -1,6 +1,6 @@ /* The OC Guy — page behaviour. - Everything here degrades: without JS the page still reads, the bands still - show their titles, the tips still list, the form still shows. */ + Everything degrades: without JS the page is a plain, readable + document with working links and a mail form. */ (function () { 'use strict'; @@ -9,23 +9,10 @@ var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; - /* ---------- signature move: the shutter on the hero photograph ---------- */ - var hero = document.querySelector('.hero-line'); - if (hero) { - if (reduce) { - hero.classList.add('shutter'); - } else { - requestAnimationFrame(function () { - setTimeout(function () { hero.classList.add('shutter'); }, 120); - }); - } - } - - /* ---------- entrance for the parts of every section ---------- */ - var targets = []; - document.querySelectorAll('.intro .statement, .open h2, .open p, .map-figure figcaption, .tips h2, .tips-note, .picker, .tips-list li, .about h2, .bio, .about-facts, .ask-line, .ask-note, .band-text, .foot-invite, .foot-links').forEach(function (el) { - targets.push(el); - }); + /* ---------- entrance for the blocks of each section ---------- */ + var targets = document.querySelectorAll( + '.lead-caption, .box, .side-list, .section-head, .card, .feature, .news li, .ask-inner' + ); if (!reduce && 'IntersectionObserver' in window) { var io = new IntersectionObserver(function (entries) { @@ -34,115 +21,56 @@ entry.target.classList.add('rv-in'); io.unobserve(entry.target); }); - }, { rootMargin: '0px 0px -12% 0px', threshold: 0.12 }); + }, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 }); - targets.forEach(function (el, i) { - el.setAttribute('data-rv', String((i % 4) + 1)); + Array.prototype.forEach.call(targets, function (el) { + el.setAttribute('data-rv', '1'); io.observe(el); }); } else { - targets.forEach(function (el) { el.classList.add('rv-in'); }); + Array.prototype.forEach.call(targets, function (el) { el.classList.add('rv-in'); }); } - /* ---------- switches: five claims ---------- */ - document.querySelectorAll('.band-switch').forEach(function (btn) { - btn.addEventListener('click', function () { - var band = btn.closest('.band'); - var on = btn.getAttribute('aria-checked') === 'true'; - btn.setAttribute('aria-checked', String(!on)); - band.setAttribute('data-on', String(!on)); - }); - }); - - /* ---------- the live thing: pick what the company does ---------- */ - var chips = Array.prototype.slice.call(document.querySelectorAll('.chip')); - var out = document.querySelector('.picker-out'); - var installOrder = ['Sales', 'CRM', 'Purchase', 'Manufacturing', 'Inventory', 'Project', 'Timesheets', 'Expenses', 'Invoicing', 'Accounting']; - - function render(mods) { - if (!mods.length) { - out.textContent = 'Choose one, or several. The modules stack in the order you would install them.'; - return; - } - out.innerHTML = ''; - mods.forEach(function (name, i) { - var span = document.createElement('span'); - span.className = 'mod'; - span.textContent = name; - out.appendChild(span); - if (i < mods.length - 1) { - var arrow = document.createElement('span'); - arrow.className = 'arrow'; - arrow.textContent = ' → '; - out.appendChild(arrow); - } - }); - var note = document.createElement('span'); - note.className = 'arrow'; - note.textContent = ' — that order, top to bottom.'; - out.appendChild(note); - } - - chips.forEach(function (chip) { - chip.setAttribute('aria-pressed', 'false'); - chip.addEventListener('click', function () { - var pressed = chip.getAttribute('aria-pressed') === 'true'; - chip.setAttribute('aria-pressed', String(!pressed)); - - var picked = chips.filter(function (c) { return c.getAttribute('aria-pressed') === 'true'; }) - .reduce(function (all, c) { - c.dataset.modules.split(',').forEach(function (m) { - var name = m.trim(); - if (all.indexOf(name) === -1) all.push(name); - }); - return all; - }, []); - - picked.sort(function (a, b) { return installOrder.indexOf(a) - installOrder.indexOf(b); }); - render(picked); - }); - }); - - /* ---------- ask: one question, by mail ---------- */ - var send = document.querySelector('.ask-send'); + /* ---------- the ask form: one question, by mail ---------- */ + var form = document.querySelector('.ask-form'); var mail = document.getElementById('ask-mail'); - if (send && mail) { - send.addEventListener('click', function () { + + if (form && mail) { + form.addEventListener('submit', function (event) { + event.preventDefault(); var addr = (mail.value || '').trim(); - var body = 'My question about Odoo Community:\n\n\n'; - if (addr) body += '\n— reply to: ' + addr + '\n'; - var href = 'mailto:o.arnold@projektbox.de' - + '?subject=' + encodeURIComponent('Odoo Community question') - + '&body=' + encodeURIComponent(body); - if (addr && addr.indexOf('@') === -1) { - mail.focus(); + if (!addr || addr.indexOf('@') === -1) { mail.setAttribute('aria-invalid', 'true'); + mail.focus(); return; } mail.removeAttribute('aria-invalid'); - window.location.href = href; - }); - mail.addEventListener('keydown', function (e) { - if (e.key === 'Enter') send.click(); + var body = 'My question about Odoo Community:\n\n\n— reply to: ' + addr + '\n'; + window.location.href = 'mailto:o.arnold@projektbox.de' + + '?subject=' + encodeURIComponent('Odoo Community question') + + '&body=' + encodeURIComponent(body); }); } - /* ---------- one moving picture: the world map drifts against the scroll ---------- */ - var map = document.getElementById('worldmap'); - if (map && !reduce) { - var ticking = false; - var update = function () { - var rect = map.getBoundingClientRect(); - var vh = window.innerHeight || 800; - if (rect.bottom > -200 && rect.top < vh + 200) { - var progress = (rect.top + rect.height / 2 - vh / 2) / vh; - map.style.transform = 'translate3d(0,' + (progress * -26).toFixed(2) + 'px,0) scale(1.06)'; - } - ticking = false; - }; - window.addEventListener('scroll', function () { - if (!ticking) { ticking = true; requestAnimationFrame(update); } - }, { passive: true }); - update(); + /* ---------- which section the reader is in ---------- */ + var links = Array.prototype.slice.call(document.querySelectorAll('.nav a[href^="#"]')); + var sections = links + .map(function (a) { return document.querySelector(a.getAttribute('href')); }) + .filter(Boolean); + + if (sections.length && 'IntersectionObserver' in window) { + var seen = new IntersectionObserver(function (entries) { + entries.forEach(function (entry) { + if (!entry.isIntersecting) return; + links.forEach(function (a) { + if (a.getAttribute('href') === '#' + entry.target.id) { + a.setAttribute('aria-current', 'true'); + } else { + a.removeAttribute('aria-current'); + } + }); + }); + }, { rootMargin: '-45% 0px -50% 0px' }); + sections.forEach(function (s) { seen.observe(s); }); } })();