diff --git a/DESIGN.md b/DESIGN.md index 39e8d18..f98fab9 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -59,7 +59,9 @@ mobile text region begins. Only the homepage particle field moves. Its transformations explain the current chapter. Reduced-motion preferences place the particles directly into each -formation. Interior pages do not use decorative animation. +formation. Coarse-pointer screens up to 820px use the same static formations to +reduce battery use during the mobile reading journey. Interior pages do not use +decorative animation. ## Language diff --git a/public/mdbase-introduction.css b/public/mdbase-introduction.css index 892706a..d26c0c9 100644 --- a/public/mdbase-introduction.css +++ b/public/mdbase-introduction.css @@ -113,13 +113,6 @@ text-underline-offset: 5px; } - .scene-count { - min-width: 52px; - color: var(--muted); - font: 520 10px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - text-align: right; - } - .scroll-progress { position: fixed; z-index: 6; @@ -440,10 +433,6 @@ font-size: 11px; } - .scene-count { - min-width: auto; - } - .chapter { padding-right: 22px; padding-left: 22px; diff --git a/public/mdbase-murmuration.js b/public/mdbase-murmuration.js index 0d41d12..ca4e6f5 100644 --- a/public/mdbase-murmuration.js +++ b/public/mdbase-murmuration.js @@ -3,8 +3,9 @@ const canvas = document.querySelector("#diagram"); const context = canvas.getContext("2d", { alpha: true }); const chapters = [...document.querySelectorAll(".chapter")]; - const sceneCount = document.querySelector("#scene-count"); - const reduceMotion = matchMedia("(prefers-reduced-motion: reduce)").matches; + const reduceMotion = + matchMedia("(prefers-reduced-motion: reduce)").matches + || matchMedia("(max-width: 820px) and (pointer: coarse)").matches; const particleCount = 361; const cellSize = 104; const particles = []; @@ -1058,8 +1059,6 @@ chapters.forEach((chapter, chapterIndex) => { chapter.classList.toggle("is-active", chapterIndex === index); }); - sceneCount.textContent = - `${String(index + 1).padStart(2, "0")} / ${String(chapters.length).padStart(2, "0")}`; buildScene(scene); } diff --git a/public/mdbase-shell.css b/public/mdbase-shell.css index e096745..9ee4e0b 100644 --- a/public/mdbase-shell.css +++ b/public/mdbase-shell.css @@ -133,12 +133,6 @@ gap: 0.35rem; } -.scene-count { - color: var(--color-text-muted); - font: 500 0.625rem/1 var(--mono); - white-space: nowrap; -} - .theme-menu { position: relative; flex: 0 0 auto; @@ -146,14 +140,14 @@ .theme-menu > summary { display: grid; - width: 2rem; - height: 2rem; + width: 2.75rem; + height: 2.75rem; padding: 0; place-items: center; color: var(--color-text-faint); background: transparent; border: 0; - border-radius: 3px; + border-radius: var(--radius); list-style: none; cursor: pointer; } @@ -199,20 +193,20 @@ padding: 0.3rem; background: var(--color-surface); border: 1px solid var(--color-border-strong); - border-radius: 3px; + border-radius: var(--radius); } .theme-menu__options button { display: grid; width: 100%; - min-height: 1.9rem; + min-height: 2.75rem; padding: 0 0.5rem; align-items: center; grid-template-columns: 1fr auto; color: var(--color-text-soft); background: transparent; border: 0; - border-radius: 2px; + border-radius: var(--radius); font: 400 0.66rem/1 var(--mono); text-align: left; cursor: pointer; @@ -251,7 +245,10 @@ } .mobile-nav summary { - padding: 0.35rem 0; + display: flex; + min-height: 2.75rem; + padding: 0.35rem 0.25rem; + align-items: center; color: var(--color-text-soft); font-size: 0.78rem; list-style: none; @@ -300,11 +297,11 @@ .site-footer__inner { display: grid; width: min(100% - 2rem, var(--site-width)); - min-height: 5.5rem; + min-height: 8rem; margin-inline: auto; - padding-block: 1.5rem; - align-items: center; - grid-template-columns: 1fr auto; + padding-block: 2rem; + align-items: start; + grid-template-columns: minmax(14rem, 0.72fr) minmax(0, 1.28fr); gap: 2rem; } @@ -319,11 +316,22 @@ font-size: 0.78rem; } -.site-footer nav { - display: flex; - flex-wrap: wrap; - justify-content: flex-end; - gap: 0.5rem 1.1rem; +.site-footer .site-footer__nav { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.5rem; +} + +.site-footer__nav > div { + display: grid; + align-content: start; + gap: 0.5rem; +} + +.site-footer__nav > div > strong { + margin-bottom: 0.15rem; + color: var(--color-text-muted); + font-size: 0.625rem; } .site-footer nav a { @@ -358,16 +366,18 @@ width: min(100% - 1.25rem, var(--site-width)); } - .scene-count { - display: none; - } - .site-footer__inner { grid-template-columns: 1fr; - gap: 1rem; + gap: 1.5rem; + } + + .site-footer .site-footer__nav { + grid-template-columns: repeat(2, minmax(0, 1fr)); } +} - .site-footer nav { - justify-content: flex-start; +@media (max-width: 30rem) { + .site-footer .site-footer__nav { + grid-template-columns: 1fr; } } diff --git a/scripts/import-spec.mjs b/scripts/import-spec.mjs index 37f3518..e948dfc 100644 --- a/scripts/import-spec.mjs +++ b/scripts/import-spec.mjs @@ -53,7 +53,8 @@ function rewrite(html, archive) { .replaceAll('href="style.css', 'href="/spec/style.css') .replaceAll('href="mdbase-favicon.svg"', 'href="/spec/mdbase-favicon.svg"') .replaceAll('href="runtime.html"', 'href="/runtime/"') - .replaceAll('href="ecosystem.html"', 'href="/implementations/"') + .replaceAll('Implementations', '') + .replaceAll('href="/testbed/"', 'href="#section-16"') .replaceAll('href="spec-v0.2.html"', 'href="/spec/v0.2/"') .replaceAll('href="spec.html"', 'href="/spec/"') .replaceAll( diff --git a/scripts/sync-sources.mjs b/scripts/sync-sources.mjs index d73b3ef..7d39b67 100644 --- a/scripts/sync-sources.mjs +++ b/scripts/sync-sources.mjs @@ -55,11 +55,15 @@ cpSync(runtimeSchemaSource, runtimeSchemaDestination, { recursive: true, force: const introductionSource = join(connectDir, "docs", "mdbase-configurations-v2.html"); required(introductionSource, "Connect introduction prototype"); const introduction = readFileSync(introductionSource, "utf8"); -const introductionStyles = extract(introduction, / diff --git a/src/pages/connect/index.astro b/src/pages/connect/index.astro index aa1f6a9..54b9328 100644 --- a/src/pages/connect/index.astro +++ b/src/pages/connect/index.astro @@ -1,314 +1,677 @@ --- import BaseLayout from "../../layouts/BaseLayout.astro"; - -const steps = [ - { - title: "The application declares what it understands", - copy: "Its bundled v1 manifest declares web callbacks or portable distribution, required domain contracts, collection kind, and any type definitions that can be installed safely." - }, - { - title: "Connect checks collection compatibility", - copy: "The local or hosted authority returns a readiness status for each collection: ready, provisionable, or incompatible. Collection contents remain private during authorization." - }, - { - title: "The user approves exact operations", - copy: "The decision names one collection and concrete operations. Contract access is scoped to records that match the declared domain contracts unless full collection access is explicitly requested." - }, - { - title: "Every request is checked at the authority", - copy: "The connector or hosted provider rechecks the current grant. Narrowing, pausing, or revoking access takes effect at the place that can open the data." - } -]; --- -
-
-
-

Private beta

-

Use your Markdown with the apps you choose.

+
+
+

Connect private beta

+

Choose which apps can use your Markdown.

+

+ When an app asks to use a collection, Connect shows whether it wants + to view, change, or delete records. You choose the collection and can + change or revoke access at any time. +

+ -
-

- Start in the app you want to use. mdbase Connect opens when that app - needs a collection, shows what it is asking for, and lets you choose - the folder or hosted collection it can use. -

- + +
+
Example access request
+
+
+
+

Access request

+

Project app

+
+ Waiting for you +
+ +
+
+ Collection + Choose where Project app can work. +
+
+ Projects + On this computer +
+ +
+
+ Permissions + Open the details to narrow access. +
+
    +
  • + + View and find records +
  • +
  • + + Create and edit records +
  • +
  • + + Delete records +
  • +
+
+ +
+

+ Project app will use Projects until you revoke access. +

+ +
-
+
-
-

For people using apps

-

Start in the app you want to use

-

- Connect keeps the same request open while it helps you sign in, pair this - computer, and choose or create a collection. -

-
    -
  1. - Open the app - Choose its Connect action. It opens a request that names the app and the access it needs. -
  2. -
  3. - Choose your collection - Use a folder on this computer or a hosted collection. Connect guides any sign-in or computer pairing along the way. -
  4. -
  5. - Review the result - See whether the app can view, edit, or delete records. You can remove permissions before approving. -
  6. -
  7. - Return to the app - Connect sends the approved collection back to the app. Pause, narrow, or revoke access at any time. -
  8. -
-
- Local folder behavior +
+
+

How it works

+

Start in the app.

- A collection kept on your computer remains ordinary Markdown in the - folder you chose. The app and account service do not receive that - folder path. + Connect keeps the request open while you sign in, choose a collection, + and decide what the app may do.

-
-
+ -
-

For developers

-

Where Connect sits

-

- Connect is optional. Applications can also use a conforming mdbase - implementation directly. Connect provides a common approval and routing - path when independent applications need access to a user's collections. -

-
    +
    1. - Application - Declares compatible collections and requests exact operations. -
    2. -
    3. - Connect - Presents the request, records the decision, and selects the route. + 01 +
      +

      An app asks for access

      +

      + The request names the app and the actions it needs. The app cannot + use a collection before you make a decision. +

      +
    4. - Collection authority - Rechecks the grant and executes each approved operation. + 02 +
      +

      You choose the collection and permissions

      +

      + Use a folder on this computer or a hosted collection. Remove any + requested permissions you do not want to grant. +

      +
    5. - mdbase collection - Remains the authoritative Markdown and collection history. + 03 +
      +

      Connect returns you to the app

      +

      + The approved access stays attached to that app and collection. You + can narrow, pause, or revoke it later. +

      +
-
-

Local and hosted collections

-

- Applications use the same SDK and operation envelopes. Routing changes - according to which connector or provider currently holds the collection's - authority. -

- - - - - - - - - - - - - - - - - - - - - - - -
AuthorityWhere Markdown livesApplication routeWhat the control plane sees
Local connectorA folder on the user's computerDirect loopback, then encrypted relay fallbackAccount, grant, operation name, routing metadata, timing, and sizes
Hosted providerEncrypted provider storage, with optional local mirrorDirect, short-lived provider capabilityAccount, grant, capability issuance, and provider routing metadata
-
- Hosted collections use a standard hosted trust model. -

- The provider must decrypt collection data to execute canonical mdbase - operations. The provider acts as a trusted data processor for hosted - collections. -

-
-
- -
-

Why one movable authority?

-

- Authority is a coordination role, not the only copy of a collection. - Writable mirrors can keep ordinary Markdown on several devices, accept - changes while offline, and synchronize them later. One authority orders - the final accepted changes, enforces collection-wide rules, and gives - every mirror the same history to converge on. -

-

- Removing that role would not remove coordination. It would require every - peer—including filesystem editors that only know how to rewrite a - Markdown file—to preserve causal history and agree continuously about - concurrent edits, renames, deletes, schema changes, revocation, and - garbage collection. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ConcernEqual peers would requireOne movable authority provides
Ordinary Markdown toolsCRDT or event metadata preserved by every writerOpen files remain the editable local format
Concurrent changesA deterministic merge for every body and frontmatter valueRevision checks, durable mutation receipts, and explicit conflicts
Renames, deletes, and configurationDistributed agreement over paths, tombstones, schemas, and constraintsOne canonical order for collection-wide decisions
Offline devicesSplit-brain reconciliation and peer-membership machineryQueued local work that safely converges when the mirror reconnects
-
- The role can move without changing the collection. +
+
+

Where your collection lives

+

Keep it on your computer or host it online.

- Authority transfer preserves the collection and record identities, - advances an authority epoch, and retires the previous authority. When - the previous authority was a local folder, that folder can continue as - a mirror. A stale authority cannot accept writes after cutover. + Apps use the same approval flow in both cases. The location of the + collection determines which component can open the records and execute + approved operations.

-
-
+ -
-

The authorization sequence

-
- { - steps.map((step) => ( -
-
-

{step.title}

-

{step.copy}

-
-
- )) - } -
-
+
+
+
+

Local folder

+

On this computer

+
+
+ Where Markdown lives +

+ In an ordinary folder you choose. The files remain available to + your text editor and other local tools. +

+
+
+ Who can read records +

+ The local connector and apps using an active grant. The Connect + control plane cannot read local operation payloads or results. +

+
+
-
-

Who can read records

-

- Connect limits the components that can read records. Local and hosted - collections have different trust boundaries. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ComponentCan read collection records?Responsibility
ApplicationOnly through its current grantUses authorized results and protects its local SDK credentials
Local connectorYes, for registered local collectionsFinal local authorization boundary and operation executor
Connect control planeNo local operation payloads or resultsIdentity, pairing, grants, short-lived tokens, and relay routing
Hosted providerYes, for hosted collectionsEncrypted storage, canonical execution, sync, and durable receipts
-
- Complete security model - Routing guide +
+
+

Hosted collection

+

Available online

+
+
+ Where Markdown lives +

+ In encrypted provider storage, with an optional local Markdown + mirror for offline work. +

+
+
+ Who can read records +

+ The hosted provider decrypts records to execute approved + operations and acts as a trusted data processor. +

+
+
-
-
-

Release status

-

Release status

-

- The private-beta stack includes local and hosted authorities, encrypted - relay operations, grant narrowing and revocation, sync, notifications, - the browser SDK, and key-bound authorization for downloaded HTML files. - The npm packages have not yet received their first public release. Public - registration, a production restore drill, signed desktop releases, and - abuse-response operations are still required before public registration. -

-
- Read the SDK documentation - - Follow release status - +
+ Permissions can change later. +

+ Narrow an app to fewer actions, pause access temporarily, or revoke the + connection. The collection remains usable after an app is removed. +

-
-

Terms used on this site

-
-
-
Grant
-
One user's approval for one application, one collection, and a concrete set of operations.
-
-
-
Authority
-
The movable coordination role that orders accepted changes, enforces collection rules, and makes the final authorization decision.
-
-
-
Control plane
-
The Connect service for identity, pairing, grants, short-lived tokens, and route coordination.
-
+
+
-
Contract
-
A portable declaration of domain behavior and its mapping to one collection type.
+

Connect private beta

+

Try Connect with your own Markdown.

+

+ The waitlist is open. Invitations will be sent in small batches when + we are ready. Joining only needs your email address. +

-
-
Operation envelope
-
A typed result containing a validity flag, result value, and structured diagnostics.
+ -
+
+ +
+ + diff --git a/src/pages/implementations/index.astro b/src/pages/implementations/index.astro deleted file mode 100644 index 064cfa0..0000000 --- a/src/pages/implementations/index.astro +++ /dev/null @@ -1,117 +0,0 @@ ---- -import BaseLayout from "../../layouts/BaseLayout.astro"; -import conformance from "../../data/conformance.json"; ---- - - -
-
-
-

mdbase v0.3

-

Implementation support

-
-
-

- mdbase v0.3 uses atomic profiles. Each row below names an artifact, - version, evidence date, and the profiles it claims. Atomic profiles - replace the legacy “Level 6” descriptions. -

- -
-
-
- -
-

Conformance claims

- - - - - - - - - - - { - conformance.implementations.map((implementation) => ( - - - - - - - )) - } - -
ArtifactVersionProfilesEvidence
- {implementation.name}
- {implementation.language} -
{implementation.version} - {implementation.profiles.map((profile) => {profile})} - { - implementation.optional_features.length > 0 && ( - Optional: {implementation.optional_features.join(", ")} - ) - } - - {implementation.evidence_date}
- {implementation.note} -
-
- -
-

Fixture coverage status

-

- An implementation claim and the shared suite's coverage status answer - different questions. The claim says what an artifact asserts it passes. - The coverage ledger says whether the specification's maintainers consider - the shared fixture set complete for that profile. -

- - - - - - - - - { - conformance.coverage.coverage_complete.map((profile) => ( - - - - - )) - } - { - conformance.coverage.draft.map((profile) => ( - - - - - )) - } - -
ProfileShared fixture coverage
{profile}Coverage complete
{profile}Draft coverage
-
- Runtime 0.2 has separate evidence. -

- The mdbase-runtime-rs row claims{" "} - runtime/0.2 for durable admission, exact bindings, - recovery, and timers. The mdbase-rs row remains a - collection-engine claim; installing the collection crate alone does - not imply workflow execution. -

-
-
-
diff --git a/src/pages/index.astro b/src/pages/index.astro index b1b4983..2914621 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -4,35 +4,43 @@ import HomeLayout from "../layouts/HomeLayout.astro"; -
+
-

mdbase v0.3 · Connect private beta

-

Different apps can use the same Markdown files, online or offline.

+

mdbase specification v0.3 · open source

+

Use the same Markdown files in different apps.

- An mdbase is a folder of ordinary Markdown files, plus a small - description of how they fit together. You can read the files - yourself, keep them where you choose, and let more than one app work - with them. + An mdbase is a folder of ordinary Markdown files that more than one + app can understand. You can open the files yourself, keep them where + you choose, and decide which apps may use them.

-

- mdbase gives independent apps a shared, durable collection of - ordinary files. The collection remains under your control. -

-
    -
  • ReadableMarkdown you can open with ordinary tools
  • -
  • UnderstandableShared definitions tell apps what records mean
  • -
  • ControlledEach app gets only the access you approve
  • -
+
+
+
Specification
+
Open rules for typed, queryable Markdown collections
+
+
+
Libraries and CLI
+
Open tools that work directly with local collections
+
+
+
Connect
+
App permissions, routing, and optional managed hosting
+
+
+
Runtime
+
Optional durable workflows, timers, and recovery
+
+
@@ -41,8 +49,9 @@ import HomeLayout from "../layouts/HomeLayout.astro";

Here is one small mdbase.

- This project collection has ordinary Markdown records, definitions - for the kinds of records it contains, and one configuration file. + This one contains project records, type definitions, and a small + configuration file. A project app, a report, and a text editor can + all use the same files.

projects/
 ├── mdbase.yaml
@@ -53,26 +62,15 @@ import HomeLayout from "../layouts/HomeLayout.astro";
 │   └── website-redesign.md
 └── people/
     └── sam.md
-

- Each record remains useful on its own. The extra files let an app - recognise a project, understand fields such as status and owner, and - check that a change is valid. -

-
    -
  • Project appUpdates status and owner through typed operations
  • -
  • Report appReads the same projects for a weekly summary
  • -
  • Text editorOpens the exact Markdown records directly
  • -
-

The files make sense to people and apps.

+

The files stay readable.

- A project record is still a normal Markdown document. A short block - at the top gives apps the facts they need; the rest is writing you can - read and edit anywhere. + Each record is still a normal Markdown document. A short block at the + top gives apps shared facts such as its title, status, and owner.

---
 type: project
@@ -84,292 +82,64 @@ owner: "[[people/sam.md]]"
 The new site should make mdbase
 easier to understand.

- The type definition stored in the same collection explains these - fields. A new app can check that it understands them before asking - you for access. + The type definition stored beside the records explains what those + fields mean. Queries, links, validation, and safe updates follow the + same open specification in every conforming tool.

-

Keep it local or host it with mdbase.

+

Keep it on your computer or host it online.

- Hosting is optional. In both cases the records remain Markdown, apps - use the same basic actions, and you decide which apps can connect. + For a local mdbase, the folder on your computer is the main copy. For + a hosted mdbase, mdbase keeps the main copy available online.

- The difference is which copy has the final say. For a local mdbase, - that is the folder on your computer. For a hosted mdbase, it is the - online copy kept by mdbase. + The open specification and local tools need no account. Connect is + the optional service for approving apps, reaching a local collection + remotely, or keeping the authoritative copy online.

-
    -
  • LocalYour folder is the main copy; your computer must be reachable
  • -
  • Hostedmdbase keeps the main copy available online
  • -
  • Either wayYou keep readable files and explicit app permissions
  • -
-

An app asks before it sees anything.

+

You choose what each app can do.

- The request names the app, the kind of collection it understands, and - every action it wants to perform. Your records stay private while you - decide. + An app asks for one collection and a specific set of actions. You + review the request before it can read or change anything.

+
+
App
Project app
+
Collection
Projects · on this computer
+
Allow
View, find, create, and edit records
+
Keep blocked
Delete records
+

- You choose one compatible collection and approve only the actions - that make sense. Permission belongs to that app and that collection; - it can be narrowed, paused, or revoked later. + The local connector enforces grants for local folders. The hosted + provider executes approved operations for hosted collections. + {" "}See the complete trust boundary.

-
    -
  1. 1Review the app and the collection it understands
  2. -
  3. 2Choose a collection and its allowed actions
  4. -
  5. 3Approve the connection or keep the collection private
  6. -
-
    -
  • ReadOpen records and see their contents
  • -
  • SearchFind matching records
  • -
  • EditCreate, update, rename, or delete records
  • -
-

The main copy stays in control.

+

Access stays under your control.

- For a local collection, the mdbase connector on your computer checks - every request before opening or changing a file. Local folder paths - stay on your computer. + Pause or remove an app's access at any time. The main copy keeps every + change in order while approved apps can keep working copies for + offline use.

-

- For a hosted collection, mdbase checks the same permission beside the - online main copy. An approved app may keep an offline copy of records - it can use, and you may keep an optional synced Markdown copy for - local tools. -

-
    -
  • Main copyOne authoritative history keeps changes in order
  • -
  • Working copiesUseful for offline and local work
  • -
  • Your controlPause or revoke an app's access at any time
  • -
- -
-
- -

Five names, one route to the files.

-

- mdbase is the collection format and specification. - The other names describe software that reads a collection, connects - applications to it, or runs workflows beside it. -

-
- -
    -
  1. - 01 -
    - Application -

    A project board, reporting tool, agent, or general collection editor.

    -
    - uses the Connect SDK -
  2. -
  3. - 02 -
    - @mdbase/connect -

    The application SDK for authorization, saved connections, routing, and typed operations.

    -
    - try it locally -
  4. -
  5. - 03 -
    - mdbase Connect -

    The optional access layer for application grants, local routing, and hosted collections.

    -
    - understand Connect -
  6. -
  7. - 04 -
    - Conforming implementation -

    Executes collection discovery, validation, queries, links, and revision-safe changes.

    -
    - compare support -
  8. -
  9. - 05 -
    - mdbase collection -

    The authoritative Markdown, YAML, types, and collection configuration.

    -
    - read the specification -
  10. -
- -

- Event and action interoperability lets - independent applications cooperate through shared contracts.{" "} - Durable runtime execution is an optional layer - for persisted workflows, timers, retries, and recovery. -

-
- -
-
- -

One task collection, three useful views.

-

- TaskNotes and the mdbase-tasknotes command line tool can use the same - task type and Markdown records. Ordinary file tools continue to read - and version those records directly. -

-
-
-
- Obsidian - TaskNotes -

Provides task views and editing inside an Obsidian vault with mdbase export enabled.

- Open TaskNotes -
-
- Terminal - mdbase-tasknotes -

Creates and queries the same tasks through the shared collection core.

- Open the CLI -
-
- Files - Editor and Git -

Read the Markdown, inspect frontmatter, and review every durable change as text.

- Read record semantics -
-
-

- The specification defines the shared collection behavior. Each tool - owns its interface and task-domain behavior. -

-
- -
-
- -

What you can use today.

-

- The specification and implementation evidence are public. Connect is - operating as a private beta while its release gates are completed. -

-
-
-
-
Specification
-
Publicmdbase v0.3, fixtures, and conformance rules
-
-
-
Implementations
-
Public sourceVersioned Rust and TypeScript profile claims
-
-
-
Connect service
-
Private betaLocal and hosted authorities are deployed for beta accounts
-
-
-
Connect SDK
-
Source buildThe npm packages await their first public release
-
-
-
- -
-
- -

Start with the outcome you need.

-
- -
- -
-
- -

Questions to settle early.

-
-
-
- Is mdbase an application, a database, or a service? -

- mdbase is a specification for collections of Markdown records. - Implementations provide database-like operations over those files. - Connect is an optional service and local connector for application - authorization and routing. -

-
-
- Does an application need mdbase Connect? -

- An application can work directly with a conforming implementation. - Connect is useful when independent applications need user-approved - access to local or hosted collections through one SDK. -

-
-
- Can an existing Markdown folder become a collection? -

- Yes. Add mdbase.yaml, then introduce type definitions - as the collection needs validation, shared field meaning, links, - queries, or lifecycle rules. -

-
-
- Who can read collection records? -

- Local records are read by the registered connector and approved - applications. Hosted records are also readable by the hosted - provider so it can execute operations. The - security model names each boundary. -

-
-
- Which path should a new developer take? -

- Run the local SDK example first. - Continue to the - Connect authorization guide - when the collection operations and revision model are familiar. -

-
-
-
diff --git a/src/pages/interop/index.astro b/src/pages/interop/index.astro deleted file mode 100644 index 22c7541..0000000 --- a/src/pages/interop/index.astro +++ /dev/null @@ -1,160 +0,0 @@ ---- -import BaseLayout from "../../layouts/BaseLayout.astro"; -import CodeBlock from "../../components/CodeBlock.astro"; - -const eventSource = `const tasknotes = bridge.connect({ - application: "tasknotes", - implementation: "tasknotes.obsidian", - version: "5.0.0" -}); - -await tasknotes.registerEventSource({ - declaration_id: "tasknotes.events", - contracts: [{ contract: taskCompletedContract }] -}); - -await tasknotes.publishEvent({ - contract: { id: "tasknotes.task.completed", version: "1.0.0" }, - subject: "Tasks/Ship%20contracts.md", - correlation_id: "workflow-42", - data: { - task_id: "task-42", - task_path: "Tasks/Ship contracts.md", - title: "Ship contracts", - status: "done", - completed_at: "2026-07-28T10:15:00Z" - } -});`; - -const actionCaller = `const outcome = await workflows.invokeAction({ - request_id: "workflow:run-42:add-card", - contract: { id: "canvas.card.create", version: "^1.0.0" }, - requested_provider: { application: "canvas-bases" }, - correlation_id: "workflow-42", - causation_id: "evt-42", - idempotency_key: "run-42:add-card", - input: { - canvas_path: "TaskNotes/Canvases/Completed tasks.canvas", - card: { kind: "file", file: "Tasks/Ship contracts.md" } - } -}); - -if (outcome.status !== "succeeded") { - throw new Error(\`\${outcome.error.message} [\${outcome.error.code}]\`); -}`; ---- - - -
-
-
-

Interoperability profile 0.1

-

Shared contracts. Independent applications.

-
-
-

- Applications can publish events and invoke actions without adopting - one universal runtime. JSON Schema proves compatibility; a separate - bridge grant supplies authority. -

- -
-
-
- -
-

The mental model

-
-
-
-

Contracts define meaning

-

- Every record, event, and action is an ordinary first-class{" "} - mdbase.contract. Its discriminated shape names the - relevant JSON Schemas: record_schema,{" "} - data_schema, or action input and outcome schemas. -

-
-
-
-
-

Applications declare one role at a time

-

- An application may be an event source, event consumer, action - caller, or action provider. A type file uses implements{" "} - only for record contracts; application declarations provide event - sources and action handlers. -

-
-
-
-
-

The bridge validates and routes

-

- Events multicast to every authorized compatible consumer. Actions - resolve to exactly one authorized provider before any side effect. - Multiple eligible providers produce ambiguous_provider{" "} - until the caller or policy selects one. -

-
-
-
-
- -
-

Events are CloudEvents 1.0

-

- The profile uses CloudEvents structured JSON and adds exact contract - version, digest, application, and implementation attributes. The event - ID survives redelivery. Correlation and causation are explicit. Event - data always validates against the exact contract artifact. -

- -
- -
-

Actions produce evidence, not a delivery fiction

-

- A logical request is admitted as an exact invocation with a selected - provider and concrete attempt. Its terminal outcome is{" "} - succeeded, rejected, failed,{" "} - cancelled, or outcome_indeterminate. Accepting a - message for delivery is never reported as successful execution. -

- -
- -
-

What stays outside the profile

- - - - - - - - - - - - - - - - - - -
In the profileOwned elsewhere
Contract identity, exact digests, envelopes, validation, resolutionWorkflow languages, scheduling, retries, and application behavior
Correlation, causation, cancellation, duplicate and indeterminate semanticsPersistence, journals, leases, recovery, and offline queues
Identity and authorization context carried to a bridgeThe user grant or transport-specific policy engine
-
-
diff --git a/src/pages/privacy/index.astro b/src/pages/privacy/index.astro index 1db4532..81aa8a2 100644 --- a/src/pages/privacy/index.astro +++ b/src/pages/privacy/index.astro @@ -14,8 +14,12 @@ import BaseLayout from "../../layouts/BaseLayout.astro";

Website

mdbase.dev is a public static documentation website. It has no account - system or advertising trackers. The hosting provider may retain ordinary - request logs for security and service operation. + system or advertising trackers. When you join the beta waitlist, mdbase + Connect retains the submitted email address and request time so the + request can be reviewed and an invitation can be sent. Rate-limit records + use keyed digests of the email and network address; the access-request + record does not store the network address. The hosting provider may also + retain ordinary request logs for security and service operation.

Connect

diff --git a/src/pages/runtime/index.astro b/src/pages/runtime/index.astro index 16947a5..08131c0 100644 --- a/src/pages/runtime/index.astro +++ b/src/pages/runtime/index.astro @@ -21,7 +21,7 @@ import BaseLayout from "../../layouts/BaseLayout.astro";

diff --git a/src/pages/sdk/contracts/index.astro b/src/pages/sdk/contracts/index.astro index 9ce3b2d..1bdf2c5 100644 --- a/src/pages/sdk/contracts/index.astro +++ b/src/pages/sdk/contracts/index.astro @@ -152,7 +152,7 @@ export class WorklogCollection {

Event sources and action providers declare executable implementations - through the interoperability bridge, not through a - type file. + through the interoperability bridge, not + through a type file.

diff --git a/src/pages/sdk/index.astro b/src/pages/sdk/index.astro index 53beba7..89688da 100644 --- a/src/pages/sdk/index.astro +++ b/src/pages/sdk/index.astro @@ -34,12 +34,12 @@ export async function listOpenTasks(client: MdbaseCollectionClient) {

Choose the first result you need

- Learn the collection API + Learn the Connect client boundary

Run typed reads and revision-safe changes against an in-memory - collection. This path needs no account or running service. + collection. This source-built sandbox needs no account or service.

- Try the SDK locally + Run the source-built sandbox
Connect a real application @@ -49,6 +49,14 @@ export async function listOpenTasks(client: MdbaseCollectionClient) {

Open the Connect guide
+
+ Implement the open specification +

+ Build a conforming collection tool or use the versioned TypeScript and + Rust implementations without the managed Connect service. +

+ Open the specification +
Private-beta release status @@ -56,7 +64,15 @@ export async function listOpenTasks(client: MdbaseCollectionClient) { The production service and protocol 1 are deployed for beta accounts. The npm packages await their first public release. Current developers build or pack the versioned artifacts from the - Connect repository. + {" "}Connect repository. +

+
+
+ Versioned implementation evidence +

+ The current site records mdbase specification 0.3.0 claims for the + TypeScript 0.3.0-rc.4 and Rust 0.4.0-rc.3 implementations, with evidence + dated 29 July 2026. Review conformance resources.

@@ -165,8 +181,8 @@ export async function listOpenTasks(client: MdbaseCollectionClient) { diff --git a/src/pages/sdk/quickstart/index.astro b/src/pages/sdk/quickstart/index.astro index 7ee1e6e..e0881e0 100644 --- a/src/pages/sdk/quickstart/index.astro +++ b/src/pages/sdk/quickstart/index.astro @@ -71,8 +71,8 @@ Markdown paths: tasks/first.md, tasks/second.md`; ---
- No account, connector, or hosted service is required. + Local sandbox · source build · no account required

This example uses the real typed collection client and the deterministic developer sandbox. It keeps every record in memory and makes no network diff --git a/src/pages/testbed/index.astro b/src/pages/testbed/index.astro deleted file mode 100644 index b01002a..0000000 --- a/src/pages/testbed/index.astro +++ /dev/null @@ -1,573 +0,0 @@ ---- -import BaseLayout from "../../layouts/BaseLayout.astro"; -import CodeBlock from "../../components/CodeBlock.astro"; - -const runCommand = `npm install --save-dev @callumalpass/mdbase-testbed - -npx mdbase-testbed validate -npx mdbase-testbed run \\ - --adapter ./scripts/testbed-adapter.mjs \\ - --evidence ./conformance/testbed-evidence.json`; - -const protocol = `your-adapter describe -# writes one adapter-description JSON document - -your-adapter run -# reads one scenario request from stdin -# writes one observable transcript to stdout`; - -const rings = [ - { - id: "contract", - number: "01", - title: "Contract", - count: 1, - summary: "One contract, one implementing type, one record, two consumers.", - scenarios: [ - ["core.shared-contract-consumers", "Both consumers receive the same validated contract view."] - ] - }, - { - id: "interop", - number: "02", - title: "Interop", - count: 11, - summary: "Live sources, consumers, callers, and providers cross an authorization boundary.", - scenarios: [ - ["interop.event-multicast", "One event reaches every compatible consumer."], - ["interop.action-single-provider", "One eligible provider is selected."], - ["interop.action-ambiguous", "Two eligible providers stop implicit dispatch."], - ["interop.action-explicit-provider", "An explicit selector resolves ambiguity."], - ["interop.provider-disappearance", "An unloaded provider is unavailable."], - ["interop.contract-digest-drift", "Same version with a different digest is rejected."], - ["interop.invalid-payload", "Schema-invalid data never reaches a handler."], - ["interop.authority-boundary", "Compatibility cannot grant authority."], - ["interop.request-replay", "A logical request is not applied twice."], - ["interop.application-provider-lifecycle", "A real app provider registers, validates, runs, and unloads."], - ["interop.application-event-multicast", "A real app source reaches two independent consumers."] - ] - }, - { - id: "runtime", - number: "03", - title: "Runtime", - count: 3, - summary: "Durable admission, recovery, and worker fencing are observed through the store.", - scenarios: [ - ["runtime.crash-recovery", "A lost outcome reuses the same attempt and invocation."], - ["runtime.competing-workers", "A stale worker cannot commit after its lease expires."], - ["runtime.application-execution", "A real app catalog produces one exact successful run."] - ] - } -]; ---- - - -

-
-
-

Portable testbed 0.1

-

Make the same promise. Prove the same behavior.

-
-
-

- One neutral suite tests TypeScript, Rust, plugins, desktop services, - and future implementations through a process boundary. Products keep - their own architecture; users get comparable evidence. -

- -
-
-
- -
-
-
-

The black-box boundary

-

The spec asks. Your implementation answers.

-
-

- Each scenario starts a fresh adapter process. Only stable facts cross - the boundary; generated IDs, database keys, and wall-clock timing stay - inside. -

-
- -
- -
-

What the testbed separates

-
-
- Owned by the specification -

The question and the oracle

-

- Draft 2020-12 schemas define neutral fixtures, run requests, adapter - descriptions, transcripts, and evidence. Every expected transcript - lives beside its scenario. -

-
-
- Owned by the implementation -

The behavior under test

-

- An adapter drives public collection, bridge, provider, runtime, and - store boundaries. It reduces observations to stable facts; it does - not reimplement the feature. -

-
-
- Owned by neither -

Authority

-

- Passing proves behavior, not permission. A transcript cannot grant - access, register a provider, publish an event, or authorize a side - effect. -

-
-
-
- -
-
-
-

15 scenarios · 3 rings

-

The portable inventory

-
-

- Small scenarios isolate failures. Application scenarios then repeat - the critical paths with real product contracts and providers. -

-
-
- { - rings.map((ring) => ( -
-
- {ring.number} -
-

{ring.title}

-

{ring.summary}

-
- {ring.count} -
-
    - { - ring.scenarios.map(([id, description]) => ( -
  • - {id} - {description} -
  • - )) - } -
-
- )) - } -
-
- -
-
-
-

Fresh process per scenario

-

Run it from any language

-
-

- The runner launches an executable directly—never through a shell—and - validates both sides of every exchange before comparing observations. -

-
-
-
-

Install and verify

- -
-
-

Implement two commands

- -
-
- -
- -
-

Evidence is precise—and deliberately limited

-

- A passing run can emit a machine-readable evidence document containing - the implementation identity, scenario IDs, profile names, and canonical - transcript digests. Attach it to a conformance claim to make the tested - behavior reproducible. -

-
- Supplemental evidence, not a blanket badge -

- A complete profile claim still runs every normative conformance test - for that profile. The testbed adds cross-language behavioral evidence; - it does not silently widen the claim. -

-
-
- - - diff --git a/src/styles/global.css b/src/styles/global.css index ae84a3d..ec7647d 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -139,6 +139,10 @@ code { outline-offset: 3px; } +main[tabindex="-1"]:focus { + outline: none; +} + .sr-only { position: absolute; width: 1px; @@ -189,7 +193,7 @@ code { .button { display: inline-flex; - min-height: 2.25rem; + min-height: 2.75rem; padding: 0.4rem 0.8rem; align-items: center; justify-content: center; @@ -408,7 +412,7 @@ code { .code-block figcaption { display: flex; - min-height: 2.25rem; + min-height: 2.75rem; padding: 0 0.75rem; align-items: center; justify-content: space-between; @@ -418,7 +422,9 @@ code { } .code-block button { - padding: 0.25rem; + min-width: 2.75rem; + min-height: 2.75rem; + padding: 0.25rem 0.4rem; color: inherit; background: transparent; border: 0; @@ -467,7 +473,7 @@ code { display: grid; margin-top: 1.25rem; border-top: 1px solid var(--line-strong); - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: repeat(3, minmax(0, 1fr)); } .start-options > div { @@ -645,7 +651,7 @@ code { color: var(--ink); background: var(--surface); border: 1px solid var(--line); - border-radius: 3px; + border-radius: var(--radius); } .docs-toc { @@ -755,7 +761,10 @@ code { } .docs-nav summary { - padding: 0.15rem; + display: flex; + min-height: 2.75rem; + padding: 0.15rem 0.4rem; + align-items: center; cursor: pointer; } @@ -775,6 +784,10 @@ code { } @media (max-width: 42rem) { + body { + font-size: 1rem; + } + .page-shell, .page-hero__inner, .content-section { diff --git a/src/styles/home.css b/src/styles/home.css index 21c3971..793d24f 100644 --- a/src/styles/home.css +++ b/src/styles/home.css @@ -30,15 +30,11 @@ } .home-body::before { - z-index: 1; - background: linear-gradient( - 90deg, - var(--paper) 0%, - var(--paper) 45%, - color-mix(in oklch, var(--paper) 96%, transparent) 50%, - color-mix(in oklch, var(--paper) 62%, transparent) 58%, - transparent 66% - ); + display: none; +} + +.home-body { + overflow-x: visible; } .home-body > main { @@ -62,7 +58,7 @@ font-weight: 650; text-decoration: none; background: var(--accent); - border-radius: 3px; + border-radius: var(--radius); transform: translateY(-160%); } @@ -89,8 +85,7 @@ transform: translateY(12px); } -.home-body .chapter__status, -.section-label { +.home-body .chapter__status { margin: 0 0 1rem; color: var(--muted); font: 500 0.625rem/1.4 var(--mono); @@ -104,313 +99,142 @@ .home-body h1, .home-body h2 { line-height: 0.98; - letter-spacing: -0.05em; + letter-spacing: -0.035em; } .home-body .chapter__actions--intro { margin-top: 1.5rem; } -.home-reference { - position: relative; - z-index: 3; - padding: clamp(5rem, 10vw, 9rem) clamp(1.25rem, 6.8vw, 6.5rem); - color: var(--ink); - background: var(--paper); - border-top: 1px solid var(--line); -} - -.home-reference + .home-reference { - border-top-color: var(--line); -} - -.home-reference__opening { - display: grid; - width: min(100%, 78rem); - margin-inline: auto; - grid-template-columns: minmax(16rem, 0.86fr) minmax(18rem, 0.65fr); - column-gap: clamp(2rem, 8vw, 8rem); +.home-body .chapter__actions { + flex-wrap: wrap; } -.home-reference__opening .section-label { - grid-column: 1 / -1; -} - -.home-body .home-reference h2 { - max-width: 13ch; - font-size: clamp(2.65rem, 6vw, 5.25rem); -} - -.home-reference__opening > p:last-child { - max-width: 55ch; - padding-top: 0.4rem; - color: var(--ink-soft); - font-size: clamp(1rem, 1.35vw, 1.125rem); - line-height: 1.62; -} - -.home-reference__opening > p strong { - color: var(--ink); -} - -.ecosystem-flow, -.availability-list, -.route-list, -.fit-guide__questions, -.tool-example { - width: min(100%, 78rem); - margin: clamp(3.5rem, 7vw, 6rem) auto 0; -} - -.ecosystem-flow { - padding: 0; - list-style: none; +.product-map, +.grant-summary { + width: min(33.75rem, 100%); + margin-top: 1.75rem; border-top: 1px solid var(--line-strong); } -.ecosystem-flow li { +.product-map > div, +.grant-summary > div { display: grid; - max-width: none; - padding: 1.3rem 0; - align-items: baseline; - grid-template-columns: 3rem minmax(0, 1fr) minmax(10rem, 0.35fr); - gap: 1rem; + padding-block: 0.7rem; border-bottom: 1px solid var(--line); -} - -.ecosystem-flow li > span, -.route-list i { - color: var(--muted); - font: 500 0.625rem/1.4 var(--mono); -} - -.ecosystem-flow li > div { - display: grid; - align-items: baseline; - grid-template-columns: minmax(10rem, 0.38fr) minmax(0, 1fr); + grid-template-columns: minmax(7.75rem, 0.72fr) minmax(0, 1.5fr); gap: 1rem; } -.ecosystem-flow strong { - font-size: 0.9rem; -} - -.ecosystem-flow p { - color: var(--ink-soft); - font-size: 0.875rem; +.product-map > div { + transition: + background-color 140ms ease-out, + border-color 140ms ease-out; } -.ecosystem-flow a, -.orientation__runtime a { - color: var(--ink); - font-size: 0.78rem; - font-weight: 700; - text-underline-offset: 0.25rem; -} - -.orientation__runtime { - width: min(100%, 78rem); - max-width: none; - margin: 1.25rem auto 0; +.product-map dt, +.grant-summary dt { color: var(--muted); - font-size: 0.78rem; + font: 500 0.625rem/1.45 var(--mono); } -.working-example { - background: var(--paper-soft); -} - -.tool-example { - display: grid; - border-top: 1px solid var(--line-strong); - grid-template-columns: repeat(3, minmax(0, 1fr)); -} - -.tool-example > div { - padding: 1.2rem 1.25rem 1.25rem 0; - border-bottom: 1px solid var(--line); -} - -.tool-example > div + div { - padding-right: 1.25rem; - padding-left: 1.25rem; - border-left: 1px solid var(--line); -} - -.tool-example > div:last-child { - padding-right: 0; -} - -.tool-example span { - display: block; - color: var(--muted); - font: 500 0.625rem/1.4 var(--mono); -} - -.tool-example strong { - display: block; - margin-top: 0.5rem; - font-size: 1rem; -} - -.tool-example p { - margin-top: 0.45rem; - color: var(--ink-soft); - font-size: 0.82rem; - line-height: 1.55; -} - -.tool-example a { - display: inline-block; - margin-top: 0.9rem; +.product-map dt a { color: var(--ink); - font-size: 0.76rem; - font-weight: 700; -} - -.working-example__note { - width: min(100%, 78rem); - max-width: none; - margin: 1.25rem auto 0; - color: var(--muted); - font-size: 0.78rem; -} - -.availability { - background: var(--paper); -} - -.availability-list { - border-top: 1px solid var(--line-strong); + font-weight: 600; + text-underline-offset: 0.24rem; } -.availability-list > div { - display: grid; - padding: 1.2rem 0; - grid-template-columns: minmax(10rem, 0.38fr) minmax(0, 1fr); - gap: 1rem; - border-bottom: 1px solid var(--line); -} - -.availability-list dt { - color: var(--muted); - font: 500 0.68rem/1.5 var(--mono); -} - -.availability-list dd { - display: grid; - grid-template-columns: minmax(8rem, 0.3fr) minmax(0, 1fr); - gap: 1rem; +.product-map dd, +.grant-summary dd { + margin: 0; color: var(--ink-soft); - font-size: 0.875rem; + font-size: 0.75rem; + line-height: 1.5; } -.availability-list dd strong { - color: var(--ink); +.product-map [data-product="connect"] :is(a, dd) { + transition: color 140ms ease-out; } -.route-list { - border-top: 1px solid var(--line-strong); -} - -.route-list a { - display: grid; - max-width: none; - padding: clamp(1.2rem, 2.5vw, 2rem) 0; - align-items: baseline; - grid-template-columns: minmax(10rem, 0.38fr) minmax(0, 1fr) 2rem; - gap: 1rem; - color: var(--ink); - border-bottom: 1px solid var(--line); - text-decoration: none; +.chapter__inner:has(.chapter__actions--intro .button:focus-visible) + .product-map + [data-product="connect"] { + background: color-mix(in oklch, var(--accent) 12%, transparent); + border-bottom-color: color-mix(in oklch, var(--accent) 48%, var(--line)); } -.route-list a:hover span, -.route-list a:hover strong { +.chapter__inner:has(.chapter__actions--intro .button:focus-visible) + .product-map + [data-product="connect"] + :is(a, dd) { color: var(--accent); } -.route-list span { - color: var(--muted); - font: 500 0.68rem/1.5 var(--mono); -} - -.route-list strong { - max-width: 46rem; - font-size: clamp(1rem, 2vw, 1.35rem); - line-height: 1.35; -} - -.route-list i { - font-style: normal; - text-align: right; -} - -.fit-guide { - background: var(--paper-soft); -} - -.fit-guide__questions { - border-top: 1px solid var(--line-strong); -} +@media (hover: hover) and (pointer: fine) { + .chapter__inner:has(.chapter__actions--intro .button:hover) + .product-map + [data-product="connect"] { + background: color-mix(in oklch, var(--accent) 12%, transparent); + border-bottom-color: color-mix(in oklch, var(--accent) 48%, var(--line)); + } -.fit-guide details { - border-bottom: 1px solid var(--line); + .chapter__inner:has(.chapter__actions--intro .button:hover) + .product-map + [data-product="connect"] + :is(a, dd) { + color: var(--accent); + } } -.fit-guide summary { - position: relative; - padding: 1.2rem 3rem 1.2rem 0; +.grant-summary dd { color: var(--ink); - font-size: clamp(0.95rem, 1.5vw, 1.1rem); - font-weight: 700; - list-style: none; - cursor: pointer; -} - -.fit-guide summary::-webkit-details-marker { - display: none; -} - -.fit-guide summary::after { - position: absolute; - top: 1.1rem; - right: 0; - color: var(--muted); - content: "+"; - font: 400 1.15rem/1 var(--mono); + font-weight: 600; } -.fit-guide details[open] summary::after { - content: "−"; -} - -.fit-guide details p { - max-width: 65ch; - padding: 0 0 1.35rem; +.home-body .chapter__body a { color: var(--ink-soft); - font-size: 0.9rem; - line-height: 1.65; + text-underline-offset: 0.22rem; } @media (max-width: 820px) { - .home-body::before { - background: linear-gradient( - 180deg, - transparent 0%, - transparent 38vh, - color-mix(in oklch, var(--paper) 44%, transparent) 44vh, - color-mix(in oklch, var(--paper) 96%, transparent) 54vh, - var(--paper) 58vh - ); + .home-body .chapter { + min-height: 128vh; + padding-bottom: 12vh; + } + + .home-body .chapter:first-child, + .home-body .chapter:last-child { + min-height: 136vh; } .home-body .chapter__inner, .home-body .chapter.is-active .chapter__inner { + position: relative; + z-index: 1; + isolation: isolate; opacity: 1; transform: none; } + .home-body .chapter__inner::before { + position: absolute; + z-index: -1; + top: -20vh; + bottom: -12vh; + left: 50%; + width: 100vw; + content: ""; + pointer-events: none; + background: linear-gradient( + 180deg, + transparent 0%, + color-mix(in oklch, var(--paper) 44%, transparent) 6vh, + color-mix(in oklch, var(--paper) 96%, transparent) 16vh, + var(--paper) 20vh + ); + transform: translateX(-50%); + } + .home-body h1 { font-size: clamp(2.75rem, 13.5vw, 4rem); } @@ -429,59 +253,14 @@ line-height: 1.72; } - .home-body .facts span { - font-size: 0.6875rem; - } - - .home-body .facts strong, .home-body .steps strong { font-size: 0.8125rem; line-height: 1.5; } - .home-reference__opening { - grid-template-columns: 1fr; - gap: 1.5rem; - } - - .home-reference__opening .section-label { - grid-column: auto; - margin-bottom: 0; - } - - .ecosystem-flow li { - grid-template-columns: 2rem minmax(0, 1fr); - } - - .ecosystem-flow li > div { - grid-template-columns: 1fr; - gap: 0.35rem; - } - - .ecosystem-flow li > a { - grid-column: 2; - } - - .availability-list > div, - .availability-list dd, - .route-list a { - grid-template-columns: 1fr; - gap: 0.35rem; - } - - .route-list i { - display: none; - } - - .tool-example { - grid-template-columns: 1fr; - } - - .tool-example > div, - .tool-example > div + div, - .tool-example > div:last-child { - padding: 1.15rem 0; - border-left: 0; + .product-map > div, + .grant-summary > div { + grid-template-columns: minmax(6.75rem, 0.62fr) minmax(0, 1.38fr); } } @@ -490,4 +269,10 @@ .home-body .record-example { font-size: 0.6875rem; } + + .product-map > div, + .grant-summary > div { + grid-template-columns: 1fr; + gap: 0.25rem; + } } diff --git a/src/styles/shell.css b/src/styles/shell.css index e096745..9ee4e0b 100644 --- a/src/styles/shell.css +++ b/src/styles/shell.css @@ -133,12 +133,6 @@ gap: 0.35rem; } -.scene-count { - color: var(--color-text-muted); - font: 500 0.625rem/1 var(--mono); - white-space: nowrap; -} - .theme-menu { position: relative; flex: 0 0 auto; @@ -146,14 +140,14 @@ .theme-menu > summary { display: grid; - width: 2rem; - height: 2rem; + width: 2.75rem; + height: 2.75rem; padding: 0; place-items: center; color: var(--color-text-faint); background: transparent; border: 0; - border-radius: 3px; + border-radius: var(--radius); list-style: none; cursor: pointer; } @@ -199,20 +193,20 @@ padding: 0.3rem; background: var(--color-surface); border: 1px solid var(--color-border-strong); - border-radius: 3px; + border-radius: var(--radius); } .theme-menu__options button { display: grid; width: 100%; - min-height: 1.9rem; + min-height: 2.75rem; padding: 0 0.5rem; align-items: center; grid-template-columns: 1fr auto; color: var(--color-text-soft); background: transparent; border: 0; - border-radius: 2px; + border-radius: var(--radius); font: 400 0.66rem/1 var(--mono); text-align: left; cursor: pointer; @@ -251,7 +245,10 @@ } .mobile-nav summary { - padding: 0.35rem 0; + display: flex; + min-height: 2.75rem; + padding: 0.35rem 0.25rem; + align-items: center; color: var(--color-text-soft); font-size: 0.78rem; list-style: none; @@ -300,11 +297,11 @@ .site-footer__inner { display: grid; width: min(100% - 2rem, var(--site-width)); - min-height: 5.5rem; + min-height: 8rem; margin-inline: auto; - padding-block: 1.5rem; - align-items: center; - grid-template-columns: 1fr auto; + padding-block: 2rem; + align-items: start; + grid-template-columns: minmax(14rem, 0.72fr) minmax(0, 1.28fr); gap: 2rem; } @@ -319,11 +316,22 @@ font-size: 0.78rem; } -.site-footer nav { - display: flex; - flex-wrap: wrap; - justify-content: flex-end; - gap: 0.5rem 1.1rem; +.site-footer .site-footer__nav { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.5rem; +} + +.site-footer__nav > div { + display: grid; + align-content: start; + gap: 0.5rem; +} + +.site-footer__nav > div > strong { + margin-bottom: 0.15rem; + color: var(--color-text-muted); + font-size: 0.625rem; } .site-footer nav a { @@ -358,16 +366,18 @@ width: min(100% - 1.25rem, var(--site-width)); } - .scene-count { - display: none; - } - .site-footer__inner { grid-template-columns: 1fr; - gap: 1rem; + gap: 1.5rem; + } + + .site-footer .site-footer__nav { + grid-template-columns: repeat(2, minmax(0, 1fr)); } +} - .site-footer nav { - justify-content: flex-start; +@media (max-width: 30rem) { + .site-footer .site-footer__nav { + grid-template-columns: 1fr; } }