wip: getting closer

This commit is contained in:
2026-01-10 23:26:30 +00:00
parent 4722094f0c
commit 7d06d18ccb
19 changed files with 475 additions and 471 deletions

View File

@@ -10,7 +10,7 @@ interface Props {
const { introduction, summary, license } = Astro.props;
---
<section id="about" class="py-20 sm:py-28 bg-gray-100 dark:bg-slate-900/30">
<section id="about" class="py-20 sm:py-28">
<div class="section-container">
<div class="max-w-3xl mx-auto">
<!-- Section header -->
@@ -23,9 +23,7 @@ const { introduction, summary, license } = Astro.props;
</div>
<!-- Introduction -->
<div class="prose-spec mb-12">
<div class="spec-content" set:html={introduction} />
</div>
<div class="prose-spec mb-12" set:html={introduction} />
<!-- Summary as feature cards -->
<div class="mb-16">
@@ -35,7 +33,7 @@ const { introduction, summary, license } = Astro.props;
>
Key Principles
</h3>
<div class="spec-content prose-spec" set:html={summary} />
<div class="prose-spec" set:html={summary} />
</div>
<!-- Feedback & License -->
@@ -65,27 +63,10 @@ const { introduction, summary, license } = Astro.props;
>
License
</h4>
<div
class="spec-content text-gray-600 dark:text-slate-400"
set:html={license}
/>
<div class="text-gray-600 dark:text-slate-400" set:html={license} />
</div>
</div>
</div>
</div>
</div>
</section>
<style>
.spec-content :global(p:last-child) {
margin-bottom: 0;
}
.spec-content :global(a) {
color: var(--color-accent);
}
.spec-content :global(a:hover) {
color: var(--color-accent-light);
}
</style>

View File

@@ -8,7 +8,7 @@ interface Props {
const { items } = Astro.props;
---
<section id="faq" class="py-20 sm:py-28 bg-gray-100 dark:bg-slate-900/30">
<section id="faq" class="py-20 sm:py-28">
<div class="section-container">
<div class="max-w-3xl mx-auto">
<!-- Section header -->
@@ -58,7 +58,7 @@ const { items } = Astro.props;
>
<div class="overflow-hidden">
<div
class="pb-6 text-gray-600 dark:text-slate-400 prose-spec spec-content"
class="pb-6 text-gray-600 dark:text-slate-400 prose-spec"
set:html={item.answer}
/>
</div>

View File

@@ -0,0 +1,33 @@
---
import { config } from "../config";
---
<footer
class="py-8 text-center text-sm
text-gray-500 dark:text-slate-500
border-t border-gray-200 dark:border-slate-800"
>
<div class="section-container">
<p>
{config.title} is authored by
<a
href={config.authorUrl}
class="hover:text-sky-600"
target="_blank"
rel="noopener noreferrer"
>
{config.author}
</a>
</p>
<p class="mt-2">
<a
href={config.license.url}
class="hover:text-sky-600"
target="_blank"
rel="noopener noreferrer"
>
{config.license.name}
</a>
</p>
</div>
</footer>

View File

@@ -43,31 +43,37 @@ const { version } = Astro.props;
<nav class="hidden md:flex items-center gap-1">
<a
href="#about"
class="inline-flex items-center px-4 py-2 text-sm font-medium rounded-lg
transition-colors cursor-pointer
class="nav-link inline-flex items-center px-4 py-2 text-sm font-medium
rounded-lg transition-colors cursor-pointer
text-gray-600 dark:text-slate-400
hover:bg-gray-100 hover:text-gray-950
dark:hover:bg-slate-800 dark:hover:text-slate-50"
data-nav-link
data-section-id="about"
>
About
</a>
<a
href="#spec"
class="inline-flex items-center px-4 py-2 text-sm font-medium rounded-lg
transition-colors cursor-pointer
class="nav-link inline-flex items-center px-4 py-2 text-sm font-medium
rounded-lg transition-colors cursor-pointer
text-gray-600 dark:text-slate-400
hover:bg-gray-100 hover:text-gray-950
dark:hover:bg-slate-800 dark:hover:text-slate-50"
data-nav-link
data-section-id="spec"
>
Spec
</a>
<a
href="#faq"
class="inline-flex items-center px-4 py-2 text-sm font-medium rounded-lg
transition-colors cursor-pointer
class="nav-link inline-flex items-center px-4 py-2 text-sm font-medium
rounded-lg transition-colors cursor-pointer
text-gray-600 dark:text-slate-400
hover:bg-gray-100 hover:text-gray-950
dark:hover:bg-slate-800 dark:hover:text-slate-50"
data-nav-link
data-section-id="faq"
>
FAQ
</a>
@@ -133,19 +139,28 @@ const { version } = Astro.props;
</div>
<a
href="#about"
class="block py-2 text-gray-600 dark:text-slate-400 hover:text-sky-600"
class="nav-link block py-2 text-gray-600 dark:text-slate-400
hover:text-sky-600"
data-nav-link
data-section-id="about"
>
About
</a>
<a
href="#spec"
class="block py-2 text-gray-600 dark:text-slate-400 hover:text-sky-600"
class="nav-link block py-2 text-gray-600 dark:text-slate-400
hover:text-sky-600"
data-nav-link
data-section-id="spec"
>
Spec
</a>
<a
href="#faq"
class="block py-2 text-gray-600 dark:text-slate-400 hover:text-sky-600"
class="nav-link block py-2 text-gray-600 dark:text-slate-400
hover:text-sky-600"
data-nav-link
data-section-id="faq"
>
FAQ
</a>
@@ -162,23 +177,22 @@ const { version } = Astro.props;
if (!header || !hero) return;
// Show/hide header based on scroll past hero
// Show header when top half of hero is above the fold
const topMargin = Math.floor(window.innerHeight / 2);
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
header.classList.add("translate-y-[-100%]");
header.classList.remove("border-gray-200", "dark:border-slate-800");
} else {
header.classList.remove("translate-y-[-100%]");
header.classList.add("border-gray-200", "dark:border-slate-800");
}
},
{ threshold: 0, rootMargin: `-${topMargin}px 0px 0px 0px` }
);
// Show/hide header based on scroll position
// Show header once scrolled down by the navbar height (64px)
const navbarHeight = 64;
observer.observe(hero);
function updateHeaderVisibility() {
if (window.scrollY >= navbarHeight) {
header.classList.remove("translate-y-[-100%]");
header.classList.add("border-gray-200", "dark:border-slate-800");
} else {
header.classList.add("translate-y-[-100%]");
header.classList.remove("border-gray-200", "dark:border-slate-800");
}
}
window.addEventListener("scroll", updateHeaderVisibility, { passive: true });
updateHeaderVisibility();
// Mobile menu toggle
if (mobileMenuBtn && mobileNav) {
@@ -193,6 +207,53 @@ const { version } = Astro.props;
});
});
}
// Active section tracking for nav links
const navLinks = document.querySelectorAll("[data-nav-link]");
const sections: { id: string; element: Element }[] = [];
// Collect unique section IDs
const seenIds = new Set<string>();
navLinks.forEach((link) => {
const id = link.getAttribute("data-section-id");
if (id && !seenIds.has(id)) {
seenIds.add(id);
const section = document.getElementById(id);
if (section) {
sections.push({ id, element: section });
}
}
});
function updateActiveNavSection() {
const headerOffset = 100;
let activeId: string | null = null;
for (const { id, element } of sections) {
const rect = element.getBoundingClientRect();
if (rect.top <= headerOffset) {
activeId = id;
}
}
navLinks.forEach((link) => {
const linkId = link.getAttribute("data-section-id");
link.classList.toggle("active", linkId === activeId);
});
}
let ticking = false;
window.addEventListener("scroll", () => {
if (!ticking) {
requestAnimationFrame(() => {
updateActiveNavSection();
ticking = false;
});
ticking = true;
}
});
updateActiveNavSection();
}
// Initialize on load

View File

@@ -219,34 +219,3 @@ const { items } = Astro.props;
// Re-initialize on Astro page transitions
document.addEventListener("astro:after-swap", initSpecSidebar);
</script>
<style>
/* Active state for sidebar links (toggled by JavaScript) */
.sidebar-link.active {
color: #0284c7;
background-color: rgba(14, 165, 233, 0.15);
}
:global(.dark) .sidebar-link.active {
color: #38bdf8;
background-color: rgba(14, 165, 233, 0.2);
}
/* Scrollbar styling */
#spec-sidebar::-webkit-scrollbar {
width: 4px;
}
#spec-sidebar::-webkit-scrollbar-track {
background: transparent;
}
#spec-sidebar::-webkit-scrollbar-thumb {
background-color: #e5e5e5;
border-radius: 2px;
}
:global(.dark) #spec-sidebar::-webkit-scrollbar-thumb {
background-color: #1e293b;
}
</style>

View File

@@ -1,12 +1,17 @@
export const config = {
title: "Git Common Flow",
title: "Git Common-Flow",
description:
"An attempt to gather a sensible selection of the most common usage " +
"patterns of git into a single and concise specification.",
author: "Jim Myhrberg",
authorUrl: "https://jimeh.me/",
hostname: "commonflow.org",
url: "https://commonflow.org",
repoUrl: "https://github.com/jimeh/common-flow",
license: {
name: "CC BY 4.0",
url: "https://creativecommons.org/licenses/by/4.0/",
},
currentVersion: "1.0.0-rc.5",
versions: [

View File

@@ -69,10 +69,10 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
"master branch".
2. The master branch MUST always be in a non-broken state with its test
suite passing.
4. The master branch IS NOT guaranteed to always work in production
3. The master branch IS NOT guaranteed to always work in production
environments. Despite test suites passing it may at times contain
unfinished work. Only releases may be considered safe for production use.
5. The master branch SHOULD always be in a "as near as possibly ready for
4. The master branch SHOULD always be in a "as near as possibly ready for
release/production" state to reduce any friction with creating a new
release.
3. Change Branches
@@ -217,7 +217,7 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
5. To create a new release from a long-term release branch, you MUST follow
the same process as a release from the master branch, except the
long-term release branch takes the place of the master branch.
7. A long-term release branch should be treated with the same respect as the
6. A long-term release branch should be treated with the same respect as the
master branch. It is effectively the master branch for the release series
in question. Meaning it MUST always be in a non-broken state, MUST NOT be
force pushed to, etc.

View File

@@ -9,6 +9,7 @@ import Hero from "../components/Hero.astro";
import AboutSection from "../components/AboutSection.astro";
import SpecSection from "../components/SpecSection.astro";
import FAQSection from "../components/FAQSection.astro";
import Footer from "../components/Footer.astro";
import { parseSpecContent } from "../utils/parseSpecContent";
import { config } from "../config";
@@ -54,34 +55,5 @@ const parsed = await parseSpecContent(markdown, version);
<FAQSection items={parsed.faq} />
</main>
<!-- Footer -->
<footer
class="py-8 text-center text-sm
text-gray-500 dark:text-slate-500
border-t border-gray-200 dark:border-slate-800"
>
<div class="section-container">
<p>
Git Common-Flow is authored by
<a
href="https://jimeh.me/"
class="hover:text-sky-600"
target="_blank"
rel="noopener noreferrer"
>
Jim Myhrberg
</a>
</p>
<p class="mt-2">
<a
href="https://creativecommons.org/licenses/by/4.0/"
class="hover:text-sky-600"
target="_blank"
rel="noopener noreferrer"
>
CC BY 4.0
</a>
</p>
</div>
</footer>
<Footer />
</SinglePage>

View File

@@ -9,6 +9,7 @@ import Hero from "../../components/Hero.astro";
import AboutSection from "../../components/AboutSection.astro";
import SpecSection from "../../components/SpecSection.astro";
import FAQSection from "../../components/FAQSection.astro";
import Footer from "../../components/Footer.astro";
import { parseSpecContent } from "../../utils/parseSpecContent";
import { config } from "../../config";
@@ -56,34 +57,5 @@ const parsed = await parseSpecContent(markdown, version);
<FAQSection items={parsed.faq} />
</main>
<!-- Footer -->
<footer
class="py-8 text-center text-sm
text-gray-500 dark:text-slate-500
border-t border-gray-200 dark:border-slate-800"
>
<div class="section-container">
<p>
Git Common-Flow is authored by
<a
href="https://jimeh.me/"
class="hover:text-sky-600"
target="_blank"
rel="noopener noreferrer"
>
Jim Myhrberg
</a>
</p>
<p class="mt-2">
<a
href="https://creativecommons.org/licenses/by/4.0/"
class="hover:text-sky-600"
target="_blank"
rel="noopener noreferrer"
>
CC BY 4.0
</a>
</p>
</div>
</footer>
<Footer />
</SinglePage>

View File

@@ -57,13 +57,13 @@ html {
font-weight: 400;
line-height: 1.7;
overflow-x: hidden;
background-color: #fafafa;
color: #0a0a0a;
background-color: theme(colors.slate.50);
color: theme(colors.slate.950);
}
.dark body {
background-color: #020617;
color: #f8fafc;
background-color: theme(colors.slate.950);
color: theme(colors.slate.50);
}
/* Typography */
@@ -77,7 +77,7 @@ html {
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.02em;
color: #0a0a0a;
color: theme(colors.slate.950);
}
.dark h1,
@@ -86,7 +86,7 @@ html {
.dark h4,
.dark h5,
.dark h6 {
color: #f8fafc;
color: theme(colors.slate.50);
}
h1 {
@@ -142,11 +142,11 @@ html {
padding: 1.25rem;
overflow-x: auto;
line-height: 1.6;
background-color: #e5e5e5;
background-color: theme(colors.slate.200);
}
.dark pre {
background-color: #0f172a;
background-color: theme(colors.slate.900);
}
pre>code {
@@ -192,11 +192,11 @@ html {
padding-left: 1.5rem;
margin: 1.5rem 0;
font-style: italic;
color: #525252;
color: theme(colors.slate.600);
}
.dark blockquote {
color: #94a3b8;
color: theme(colors.slate.400);
}
/* Strong text */
@@ -204,16 +204,6 @@ html {
font-weight: 600;
}
/* Selection */
::selection {
background-color: rgba(14, 165, 233, 0.2);
color: #0a0a0a;
}
.dark ::selection {
color: #f8fafc;
}
/* Focus styles */
:focus-visible {
outline: 2px solid var(--color-accent);
@@ -340,6 +330,7 @@ html {
/* Component styles */
@layer components {
/* Section container - uses CSS vars, keep here */
.section-container {
max-width: calc(var(--content-max-width) + var(--sidebar-width) + 4rem);
@@ -357,11 +348,11 @@ html {
margin-top: 3rem;
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid #e5e5e5;
border-bottom: 1px solid theme(colors.slate.200);
}
.dark .prose-spec h2 {
border-bottom-color: #1e293b;
border-bottom-color: theme(colors.slate.800);
}
.prose-spec h2:first-child {
@@ -373,35 +364,35 @@ html {
font-weight: 600;
margin-top: 2rem;
margin-bottom: 1rem;
color: #525252;
color: theme(colors.slate.600);
}
.dark .prose-spec h3 {
color: #94a3b8;
color: theme(colors.slate.400);
}
.prose-spec p {
color: #525252;
color: theme(colors.slate.600);
}
.dark .prose-spec p {
color: #94a3b8;
color: theme(colors.slate.400);
}
.prose-spec strong {
color: #0a0a0a;
color: theme(colors.slate.950);
}
.dark .prose-spec strong {
color: #f8fafc;
color: theme(colors.slate.50);
}
.prose-spec li {
color: #525252;
color: theme(colors.slate.600);
}
.dark .prose-spec li {
color: #94a3b8;
color: theme(colors.slate.400);
}
/* Nested ordered list counters (spec numbering: 1., 1.1., 1.2.) */
@@ -411,19 +402,19 @@ html {
list-style: none;
}
.prose-spec ol > li {
.prose-spec ol>li {
counter-increment: item;
position: relative;
}
.prose-spec ol > li::before {
.prose-spec ol>li::before {
content: counters(item, ".") ".";
position: absolute;
left: -2.5rem;
width: 2rem;
text-align: right;
font-weight: 500;
color: #737373;
color: theme(colors.slate.500);
}
.prose-spec img {
@@ -432,4 +423,48 @@ html {
border-radius: 8px;
margin: 2rem 0;
}
.prose-spec p:last-child {
margin-bottom: 0;
}
/* Nav link active state (toggled by JavaScript) */
.nav-link.active {
color: theme(colors.sky.600) !important;
background-color: theme(colors.sky.500 / 15%) !important;
}
.dark .nav-link.active {
color: theme(colors.sky.400) !important;
background-color: theme(colors.sky.500 / 20%) !important;
}
/* Sidebar link active state (toggled by JavaScript) */
.sidebar-link.active {
color: theme(colors.sky.600) !important;
background-color: theme(colors.sky.500 / 15%) !important;
}
.dark .sidebar-link.active {
color: theme(colors.sky.400) !important;
background-color: theme(colors.sky.500 / 20%) !important;
}
/* Sidebar scrollbar styling */
#spec-sidebar::-webkit-scrollbar {
width: 4px;
}
#spec-sidebar::-webkit-scrollbar-track {
background: transparent;
}
#spec-sidebar::-webkit-scrollbar-thumb {
background-color: theme(colors.slate.200);
border-radius: 2px;
}
.dark #spec-sidebar::-webkit-scrollbar-thumb {
background-color: theme(colors.slate.800);
}
}