diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..917aa9d7 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,57 @@ +{ + "permissions": { + "allow": [ + "Bash(lsof -ti:3000)", + "Bash(node capture.mjs)", + "Bash(node compare.mjs)", + "Bash(open /Users/john/projects/very_good_workflows/tools/visual-compare/screenshots/report.html)", + "Bash(node /private/tmp/check-h1.mjs)", + "Bash(node check-h1.mjs)", + "Bash(lsof -ti:4000)", + "Bash(npx serve build/jaspr -l 4000)", + "Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:4000/docs/overview/)", + "Bash(node capture.mjs --jaspr-only)", + "Bash(node inspect-dark.mjs)", + "Bash(node inspect-spr.mjs)", + "Bash(node inspect-width.mjs)", + "Bash(dart run jaspr_cli:jaspr build)", + "Bash(pkill -f build_daemon)", + "Bash(pkill -f dart.*build_runner)", + "Bash(curl -s http://localhost:4000/docs/overview/)", + "mcp__plugin_wingspan_dart__dart_fix", + "Bash(node capture.js --url=\"http://localhost:4000/docs/workflows\" --name=\"docs-workflows\" --width=1200)", + "Bash(node capture.mjs --url=\"http://localhost:3000/docs/workflows\" --name=\"docs-workflows\" --width=1200)", + "Bash(node capture.mjs --url=\"http://localhost:4000/docs/workflows\" --name=\"docs-workflows\" --width=1200)", + "mcp__plugin_wingspan_dart__hover", + "Bash(jaspr serve --port 4000)", + "Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:4000/docs/workflows)", + "Bash(node /tmp/measure-layout.mjs)", + "Bash(npm run capture:jaspr)", + "Bash(npm run compare)", + "Bash(node analyze-diffs.mjs)", + "Bash(ls /Users/john/projects/very_good_workflows/tools/visual-compare/screenshots/diff-*.png)", + "Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:4000/)", + "Bash(node measure.mjs)", + "Bash(node measure-home.mjs)", + "Bash(node measure-buttons.mjs)", + "Read(//Users/john/.pub-cache/hosted/pub.dev/jaspr_content-0.5.0/lib/components/**)", + "Bash(curl -s http://localhost:4000/)", + "Read(//Users/john/.pub-cache/hosted/pub.dev/jaspr_content-0.5.0/**)", + "Read(//Users/john/.pub-cache/hosted/pub.dev/jaspr-0.22.3/lib/src/dom/**)", + "Read(//Users/john/.pub-cache/hosted/pub.dev/jaspr-0.22.3/lib/**)", + "Bash(grep -n 'final class li\\\\|final class ul' /Users/john/.pub-cache/hosted/pub.dev/jaspr-0.22.3/lib/src/dom/html/*.dart)", + "Bash(while read hash msg)", + "Bash(do echo \"=== $msg ===\")", + "Bash(git diff-tree --no-commit-id --name-only -r \"$hash\")", + "Bash(done)", + "Bash(python3 -c \"import sys,json; h=json.load\\(sys.stdin\\); [print\\(f\"\"{e[''timestamp''][:16]} {e[''parityScore'']}%\"\"\\) for e in h]\")", + "Bash(node -e \":*)", + "Bash(xargs kill -9)", + "Bash(lsof -ti:5467)", + "Bash(pkill -9 -f 'dart.*build_daemon')", + "Bash(python3 -m json.tool)", + "Bash(ls /Users/john/projects/very_good_workflows/tools/visual-compare/screenshots/parity*)", + "Bash(ls /Users/john/projects/very_good_workflows/tools/visual-compare/parity*)" + ] + } +} diff --git a/.github/cspell.json b/.github/cspell.json index b011c3d7..82b89885 100644 --- a/.github/cspell.json +++ b/.github/cspell.json @@ -15,28 +15,46 @@ } ], "useGitignore": true, + "ignorePaths": [ + "**/*.tmLanguage.json" + ], "words": [ "amannn", "APPDATA", "brickhub", + "Consolas", "creatordate", "creds", "clsx", "Csvg", "Contador", "endtemplate", + "evenodd", + "flexbox", + "frontmatter", "Infima", + "jaspr", "linecap", + "linejoin", "localizable", + "Menlo", "mostrado", "noopener", + "noreferrer", + "Noto", + "Octicon", "pΓ‘gina", "peaceiris", + "Scrollspy", + "Segoe", "srealmoreno", "retag", "Texto", "typecheck", + "unhighlighted", + "viewports", "webfactory", - "xlink" + "xlink", + "zoomable" ] } diff --git a/JASPR_MIGRATION.md b/JASPR_MIGRATION.md new file mode 100644 index 00000000..fefdae64 --- /dev/null +++ b/JASPR_MIGRATION.md @@ -0,0 +1,70 @@ +# Migrating from Docusaurus to Jaspr + +## 1. Scaffold the Jaspr project + +```bash +dart pub global activate jaspr_cli +jaspr create --template=docs site_jaspr +``` + +This gives you a Jaspr static site pre-configured with `jaspr_content`, which includes a `DocsLayout` with sidebar, header, dark/light mode, markdown parsing, and syntax highlighting out of the box. + +## 2. Migrate your content + +Your current docs are 10 Markdown files in `site/docs/` with YAML frontmatter (`sidebar_position`, `title`, `description`). These can largely be copied over as-is -- `jaspr_content` uses a `FilesystemLoader` that reads `.md` files from a content directory and supports frontmatter. + +The current sidebar is auto-generated from the filesystem in Docusaurus. In Jaspr, the sidebar is defined explicitly in Dart code: + +```dart +sidebar: Sidebar(groups: [ + SidebarGroup(links: [ + SidebarLink(text: 'Overview', href: '/'), + ]), + SidebarGroup(title: 'Workflows', links: [ + SidebarLink(text: 'Dart Package', href: '/workflows/dart_package'), + SidebarLink(text: 'Flutter Package', href: '/workflows/flutter_package'), + // ... etc + ]), +]), +``` + +## 3. Recreate the custom homepage + +The current homepage (`site/src/pages/index.tsx`) is a custom React component with a hero banner, CTA button, and blog section. In Jaspr, you'd build this as a `StatelessComponent` using Jaspr's HTML element functions (`div`, `a`, `img`, etc.) with typed `Styles` for CSS. + +## 4. Migrate theming + +The custom CSS (`site/src/css/custom.css`) defines brand colors, Poppins font, and dark mode overrides. In Jaspr, theming is done via `ContentTheme`: + +```dart +theme: ContentTheme( + primary: ThemeColor(Color('#2a48df'), dark: Color('#66fbd1')), + background: ThemeColor(Colors.white, dark: Color('#0b0d0e')), +), +``` + +## 5. Update the deployment workflow + +Replace the Node/Docusaurus build steps in `.github/workflows/site_deploy.yaml` with Dart/Jaspr: + +```yaml +- uses: dart-lang/setup-dart@v1 +- run: dart pub global activate jaspr_cli +- run: jaspr build +# Output goes to build/jaspr/ instead of site/build/ +``` + +## Key differences to be aware of + +| Aspect | Docusaurus (current) | Jaspr | +|---|---|---| +| Sidebar | Auto-generated from filesystem | Manual definition in Dart | +| Custom pages | React/TSX components | Dart `StatelessComponent` | +| Styling | CSS/CSS Modules | Typed `Styles` in Dart + optional CSS | +| Markdown extensions | MDX (JSX in Markdown) | Custom components registered in Dart | +| Search | Algolia integration available | No built-in search | +| Docs versioning | Built-in | Not available | + +## Tradeoffs + +The main benefit is staying entirely within the Dart ecosystem. The main costs are losing Docusaurus's auto-generated sidebar, built-in search integration, and the broader plugin ecosystem. The site is relatively straightforward (10 doc pages, one custom homepage), so the migration scope is manageable. diff --git a/site_jaspr/.gitignore b/site_jaspr/.gitignore new file mode 100644 index 00000000..b7ea95fd --- /dev/null +++ b/site_jaspr/.gitignore @@ -0,0 +1,30 @@ +# Files and directories created by pub. +**/doc/api/ +.dart_tool/ +.packages + +# Conventional directory for build output. +/build/ + +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ diff --git a/site_jaspr/README.md b/site_jaspr/README.md new file mode 100644 index 00000000..eef2407a --- /dev/null +++ b/site_jaspr/README.md @@ -0,0 +1,15 @@ +# site_jaspr + +A documentation site built with Jaspr + +## Running the project + +Run your project using `jaspr serve`. + +The development server will be available on `http://localhost:8080`. + +## Building the project + +Build your project using `jaspr build`. + +The output will be located inside the `build/jaspr/` directory. diff --git a/site_jaspr/analysis_options.yaml b/site_jaspr/analysis_options.yaml new file mode 100644 index 00000000..1198b97b --- /dev/null +++ b/site_jaspr/analysis_options.yaml @@ -0,0 +1,46 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +analyzer: +# exclude: +# - path/to/excluded/files/** + +# Jaspr has a custom analyzer plugin 'jaspr_lints', which is enabled here. +# +# You can toggle Jaspr specific lint rules in the 'diagnostics' section below. +plugins: + jaspr_lints: + version: ^0.6.0 + diagnostics: + prefer_html_components: true + sort_children_last: true + styles_ordering: true + +# Uncomment the following section to enable or disable additional rules. + +# linter: +# rules: +# camel_case_types: true + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +formatter: + # Change this to your preferred line length. + page_width: 120 + trailing_commas: preserve + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/site_jaspr/content/_data/links.yaml b/site_jaspr/content/_data/links.yaml new file mode 100644 index 00000000..221c43df --- /dev/null +++ b/site_jaspr/content/_data/links.yaml @@ -0,0 +1,3 @@ +website: https://verygood.ventures +devTools: https://developer.vgv.dev +github: https://github.com/VeryGoodOpenSource/very_good_workflows diff --git a/site_jaspr/content/_data/site.yaml b/site_jaspr/content/_data/site.yaml new file mode 100644 index 00000000..78ad681a --- /dev/null +++ b/site_jaspr/content/_data/site.yaml @@ -0,0 +1,3 @@ +# Site Configuration +titleBase: Very Good Workflows +favicon: favicon.ico diff --git a/site_jaspr/content/docs/category/workflows.md b/site_jaspr/content/docs/category/workflows.md new file mode 100644 index 00000000..4b839938 --- /dev/null +++ b/site_jaspr/content/docs/category/workflows.md @@ -0,0 +1,8 @@ +--- +title: "Workflows" +--- + + + + +If you are not redirected automatically, [click here](/docs/workflows). diff --git a/site_jaspr/content/docs/overview.md b/site_jaspr/content/docs/overview.md new file mode 100644 index 00000000..ec9c7a29 --- /dev/null +++ b/site_jaspr/content/docs/overview.md @@ -0,0 +1,45 @@ +--- +title: Overview +description: A collection of reusable GitHub workflows used at Very Good Ventures. +image: /images/meta/open-graph.png +--- + +# Overview + +[GitHub workflows][github_workflows_link] are configurable, automated processes that can run at various points during the development process. For example, a workflow can run when a pull request is created or updated to perform various code quality checks before allowing the changes to be merged. + +Very Good Workflows is a collection of workflows that we use at VGV to run automated checks in our CI pipelines. While built by VGV to be used internally, they can be used by anyone. + +## Quick Start π + +To get started, add Very Good Workflows to an existing GitHub workflow: + +```yaml +# A reusable workflow for Dart packages +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 + +# A reusable workflow for Flutter packages +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + +# A reusable workflow for ensuring commits are semantic +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/semantic_pull_request.yml@v1 + +# A reusable workflow for verifying package scores on pub.dev +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/pana.yml@v1 + +# A reusable workflow for running a spell check +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/spell_check.yml@v1 + +# A reusable workflow for publishing Flutter packages +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_pub_publish.yml@v1 + +# A reusable workflow for publishing Dart packages +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_pub_publish.yml@v1 + +# A reusable workflow for publishing Mason bricks +uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/mason_publish.yml@v1 + +``` + +[github_workflows_link]: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +[very_good_ventures_link]: https://verygood.ventures diff --git a/site_jaspr/content/docs/workflows.md b/site_jaspr/content/docs/workflows.md new file mode 100644 index 00000000..ed7703fa --- /dev/null +++ b/site_jaspr/content/docs/workflows.md @@ -0,0 +1,47 @@ +--- +title: "Workflows" +description: "Learn about all of the workflows that Very Good Workflows supports." +--- + +# Workflows π¦ + +Learn about all of the workflows that Very Good Workflows supports. + +
This workflow runs helpful checks on a Dart package according to the steps below. As with any workflow, it can be customized.
+ + +We use this workflow to publish a Dart package to pub.dev.
+ + +This workflow runs helpful checks on a Flutter package according to the steps below. As with any workflow, it can be customized.
+ + +We use this workflow to publish a Flutter package to pub.dev.
+ + +At VGV, we keep track of the rights and restrictions external dependencies might impose on Dart or Flutter projects.
+ + +We use this workflow to publish a brick to brickhub.dev.
+ + +We use the Dart tool pana to validate that your pub score will be the max possible score before publishing a package to pub.dev.
+ + +At VGV, we use conventional commits. This way, our commit history is clean and useful when we squash and merge β we can easily see the type and brief description of each PR.
+ + +We use cspell for basic spell check on our projects.
+ +` formatting in TOC entries.
+///
+/// Headings written with backticks in markdown (e.g. `` ### `types` ``) will
+/// render their TOC link text inside a `` element, matching Docusaurus
+/// behavior where code-styled headings look visually distinct from regular
+/// headings in the table of contents.
+class CodeAwareTocPostProcessor implements PageExtension {
+ static final _headerRegex = RegExp(r'^h(\d)$', caseSensitive: false);
+
+ @override
+ Future> apply(Page page, List nodes) async {
+ final codeHeadingIds = {};
+ _findCodeHeadings(nodes, codeHeadingIds);
+
+ if (codeHeadingIds.isNotEmpty) {
+ final toc = page.data['toc'];
+ if (toc is TableOfContents) {
+ page.apply(data: {
+ 'toc': _CodeAwareTableOfContents(toc.entries, codeHeadingIds),
+ });
+ }
+ }
+
+ return nodes;
+ }
+
+ void _findCodeHeadings(List nodes, Set codeIds) {
+ for (final node in nodes) {
+ if (node is ElementNode) {
+ if (_headerRegex.hasMatch(node.tag)) {
+ final id = node.attributes['id'];
+ if (id != null && _hasCodeChild(node)) {
+ codeIds.add(id);
+ }
+ }
+ if (node.children != null) {
+ _findCodeHeadings(node.children!, codeIds);
+ }
+ }
+ }
+ }
+
+ bool _hasCodeChild(ElementNode node) {
+ for (final child in node.children ?? const []) {
+ if (child is ElementNode) {
+ if (child.tag == 'code') return true;
+ if (_hasCodeChild(child)) return true;
+ }
+ }
+ return false;
+ }
+}
+
+class _CodeAwareTableOfContents extends TableOfContents {
+ _CodeAwareTableOfContents(super.entries, this._codeEntryIds);
+
+ final Set _codeEntryIds;
+
+ @override
+ Component build() {
+ return ul([..._buildToc(entries)]);
+ }
+
+ Iterable _buildToc(List toc, [int indent = 0]) sync* {
+ for (final entry in toc) {
+ final isCode = _codeEntryIds.contains(entry.id);
+ yield li(
+ styles: Styles(padding: Padding.only(left: (0.75 * indent).em)),
+ [
+ Builder(
+ builder: (context) {
+ final route = RouteState.of(context);
+ return a(
+ href: '${route.path}#${entry.id}',
+ [
+ if (isCode)
+ code([Component.text(entry.text)])
+ else
+ Component.text(entry.text),
+ ],
+ );
+ },
+ ),
+ ],
+ );
+ if (entry.children.isNotEmpty) {
+ yield* _buildToc(entry.children, indent + 1);
+ }
+ }
+ }
+}
diff --git a/site_jaspr/lib/main.client.dart b/site_jaspr/lib/main.client.dart
new file mode 100644
index 00000000..224a0fe3
--- /dev/null
+++ b/site_jaspr/lib/main.client.dart
@@ -0,0 +1,28 @@
+/// The entrypoint for the **client** environment.
+///
+/// The [main] method will only be executed on the client when loading the page.
+/// To run code on the server during pre-rendering, check the `main.server.dart` file.
+library;
+
+// Client-specific Jaspr import.
+import 'package:jaspr/client.dart';
+
+// This file is generated automatically by Jaspr, do not remove or edit.
+import 'main.client.options.dart';
+
+void main() {
+ // Initializes the client environment with the generated default options.
+ Jaspr.initializeApp(
+ options: defaultClientOptions,
+ );
+
+ // Starts the app.
+ //
+ // [ClientApp] automatically loads and renders all components annotated with @client.
+ //
+ // You can wrap this with additional [InheritedComponent]s to share state across multiple
+ // @client components if needed.
+ runApp(
+ const ClientApp(),
+ );
+}
diff --git a/site_jaspr/lib/main.client.options.dart b/site_jaspr/lib/main.client.options.dart
new file mode 100644
index 00000000..99f59f61
--- /dev/null
+++ b/site_jaspr/lib/main.client.options.dart
@@ -0,0 +1,57 @@
+// dart format off
+// ignore_for_file: type=lint
+
+// GENERATED FILE, DO NOT MODIFY
+// Generated with jaspr_builder
+
+import 'package:jaspr/client.dart';
+
+import 'package:jaspr_content/components/_internal/code_block_copy_button.dart'
+ deferred as _code_block_copy_button;
+import 'package:jaspr_content/components/_internal/zoomable_image.dart'
+ deferred as _zoomable_image;
+import 'package:jaspr_content/components/sidebar_toggle_button.dart'
+ deferred as _sidebar_toggle_button;
+import 'package:jaspr_content/components/theme_toggle.dart'
+ deferred as _theme_toggle;
+
+/// Default [ClientOptions] for use with your Jaspr project.
+///
+/// Use this to initialize Jaspr **before** calling [runApp].
+///
+/// Example:
+/// ```dart
+/// import 'main.client.options.dart';
+///
+/// void main() {
+/// Jaspr.initializeApp(
+/// options: defaultClientOptions,
+/// );
+///
+/// runApp(...);
+/// }
+/// ```
+ClientOptions get defaultClientOptions => ClientOptions(
+ clients: {
+ 'jaspr_content:code_block_copy_button': ClientLoader(
+ (p) => _code_block_copy_button.CodeBlockCopyButton(),
+ loader: _code_block_copy_button.loadLibrary,
+ ),
+ 'jaspr_content:zoomable_image': ClientLoader(
+ (p) => _zoomable_image.ZoomableImage(
+ src: p['src'] as String,
+ alt: p['alt'] as String?,
+ caption: p['caption'] as String?,
+ ),
+ loader: _zoomable_image.loadLibrary,
+ ),
+ 'jaspr_content:sidebar_toggle_button': ClientLoader(
+ (p) => _sidebar_toggle_button.SidebarToggleButton(),
+ loader: _sidebar_toggle_button.loadLibrary,
+ ),
+ 'jaspr_content:theme_toggle': ClientLoader(
+ (p) => _theme_toggle.ThemeToggle(),
+ loader: _theme_toggle.loadLibrary,
+ ),
+ },
+);
diff --git a/site_jaspr/lib/main.server.dart b/site_jaspr/lib/main.server.dart
new file mode 100644
index 00000000..29854754
--- /dev/null
+++ b/site_jaspr/lib/main.server.dart
@@ -0,0 +1,243 @@
+/// The entrypoint for the **server** environment.
+///
+/// The [main] method will only be executed on the server during pre-rendering.
+/// To run code on the client, check the `main.client.dart` file.
+library;
+
+import 'dart:io';
+
+// Server-specific Jaspr import.
+import 'package:jaspr/dom.dart';
+import 'package:jaspr/server.dart';
+
+import 'package:jaspr_content/components/header.dart';
+import 'package:jaspr_content/components/image.dart';
+import 'package:jaspr_content/components/sidebar.dart';
+
+import 'components/collapsible_sidebar.dart';
+import 'package:jaspr_content/components/theme_toggle.dart';
+import 'package:jaspr_content/jaspr_content.dart';
+import 'package:jaspr_content/theme.dart';
+
+import 'components/breadcrumb.dart';
+import 'components/doc_callout.dart';
+import 'extensions/code_aware_toc.dart';
+import 'components/edit_page_link.dart';
+import 'components/homepage_layout.dart';
+import 'components/icon_link.dart';
+import 'components/nav_link.dart';
+import 'components/page_navigation.dart';
+import 'components/safe_code_block.dart';
+import 'components/site_footer.dart';
+
+// This file is generated automatically by Jaspr, do not remove or edit.
+import 'main.server.options.dart';
+
+void main() {
+ // Initializes the server environment with the generated default options.
+ Jaspr.initializeApp(
+ options: defaultServerOptions,
+ );
+
+ // Starts the app.
+ runApp(
+ ContentApp(
+ templateEngine: MustacheTemplateEngine(),
+ parsers: [
+ MarkdownParser(),
+ ],
+ extensions: [
+ HeadingAnchorsExtension(),
+ TableOfContentsExtension(),
+ CodeAwareTocPostProcessor(),
+ ],
+ components: [
+ DocCallout(),
+ SafeCodeBlock(
+ grammars: {
+ 'yaml': File('grammars/yaml.tmLanguage.json').readAsStringSync(),
+ 'bash': File('grammars/bash.tmLanguage.json').readAsStringSync(),
+ 'json': File('grammars/json.tmLanguage.json').readAsStringSync(),
+ },
+ ),
+ Image(zoom: true),
+ ],
+ layouts: [
+ DocsLayout(
+ header: Header(
+ title: 'Very Good Workflows',
+ logo: '/images/workflows_nav_icon.svg',
+ items: [
+ NavLink(text: 'Get Started', href: '/docs/overview', isButton: true),
+ NavLink(text: 'VGV Dev Tools', href: 'https://verygood.ventures/dev'),
+ IconLink(
+ href: 'https://verygood.ventures',
+ iconSrc: '/images/vgv_logo_black.svg',
+ darkIconSrc: '/images/vgv_logo_fill.svg',
+ alt: 'Very Good Ventures',
+ ),
+ IconLink(
+ href: 'https://github.com/VeryGoodOpenSource/very_good_workflows',
+ iconSrc: '/images/github.svg',
+ darkIconSrc: '/images/github_white.svg',
+ alt: 'GitHub',
+ ),
+ ThemeToggle(),
+ ],
+ ),
+ sidebar: CollapsibleSidebar(
+ // Mobile sidebar header: CTA button + ThemeToggle, matching
+ // Docusaurus's navbar-sidebar primary row (CTA Β· toggle Β· Γ).
+ // Icon links and secondary text links are intentionally omitted
+ // to keep the header to a single row.
+ mobileNavItems: [
+ a(
+ href: '/docs/workflows',
+ classes: 'mobile-workflows-btn',
+ [
+ img(
+ src: '/images/workflows_nav_icon.svg',
+ alt: 'Workflows',
+ attributes: {'height': '32', 'width': '105'},
+ ),
+ ],
+ ),
+ ThemeToggle(),
+ ],
+ // Primary panel: global nav items shown when "β Back to main menu"
+ // is tapped β mirrors Docusaurus's primary sidebar panel.
+ primaryNavItems: [
+ a(
+ href: '/docs/overview',
+ classes: 'sidebar-link',
+ [Component.text('Get Started')],
+ ),
+ a(
+ href: 'https://verygood.ventures/dev',
+ target: Target.blank,
+ classes: 'sidebar-link',
+ [Component.text('VGV Dev Tools')],
+ ),
+ a(
+ href: 'https://verygood.ventures',
+ target: Target.blank,
+ classes: 'sidebar-link',
+ [
+ img(
+ classes: 'sidebar-icon-light',
+ src: '/images/vgv_logo_black.svg',
+ alt: 'Very Good Ventures',
+ attributes: {'width': '24', 'height': '24'},
+ ),
+ img(
+ classes: 'sidebar-icon-dark',
+ src: '/images/vgv_logo_fill.svg',
+ alt: 'Very Good Ventures',
+ attributes: {'width': '24', 'height': '24'},
+ ),
+ ],
+ ),
+ a(
+ href: 'https://github.com/VeryGoodOpenSource/very_good_workflows',
+ target: Target.blank,
+ classes: 'sidebar-link',
+ [
+ img(
+ classes: 'sidebar-icon-light',
+ src: '/images/github.svg',
+ alt: 'GitHub',
+ attributes: {'width': '24', 'height': '24'},
+ ),
+ img(
+ classes: 'sidebar-icon-dark',
+ src: '/images/github_white.svg',
+ alt: 'GitHub',
+ attributes: {'width': '24', 'height': '24'},
+ ),
+ ],
+ ),
+ ],
+ items: [
+ SidebarEntry(text: 'Overview', href: '/docs/overview'),
+ SidebarEntry(
+ text: 'Workflows',
+ href: '/docs/workflows',
+ children: [
+ SidebarLink(text: 'Dart Package', href: '/docs/workflows/dart_package'),
+ SidebarLink(text: 'Dart Pub Publish', href: '/docs/workflows/dart_pub_publish'),
+ SidebarLink(text: 'Flutter Package', href: '/docs/workflows/flutter_package'),
+ SidebarLink(text: 'Flutter Pub Publish', href: '/docs/workflows/flutter_pub_publish'),
+ SidebarLink(text: 'License Check', href: '/docs/workflows/license_check'),
+ SidebarLink(text: 'Mason Publish', href: '/docs/workflows/mason_publish'),
+ SidebarLink(text: 'Pana', href: '/docs/workflows/pana'),
+ SidebarLink(text: 'Semantic Pull Request', href: '/docs/workflows/semantic_pull_request'),
+ SidebarLink(text: 'Spell Check', href: '/docs/workflows/spell_check'),
+ ],
+ ),
+ ],
+ ),
+ footer: Component.fragment([
+ const Breadcrumb(),
+ const EditPageLink(),
+ const PageNavigation(),
+ const SiteFooter(),
+ ]),
+ ),
+ const HomepageLayout(),
+ ],
+ theme: ContentTheme.raw(
+ colors: [
+ // Exact Docusaurus/Infima color values from site/src/css/custom.css.
+ ColorToken('primary', Color('#2a48df'), dark: Color('#66fbd1')),
+ ColorToken('background', Color('#fbfcff'), dark: Color('#020f30')),
+ ColorToken('text', Color('#1c1e21'), dark: Color('#e3e3e3')),
+ ColorToken('content-headings', Color('#1c1e21'), dark: Color('#ffffff')),
+ ColorToken('content-lead', Color('#606770'), dark: Color('#a0a0a0')),
+ ColorToken('content-links', Color('#2a48df'), dark: Color('#66fbd1')),
+ ColorToken('content-bold', Color('#1c1e21'), dark: Color('#ffffff')),
+ ColorToken('content-counters', Color('#606770'), dark: Color('#a0a0a0')),
+ ColorToken('content-bullets', Color('#dadde1'), dark: Color('#444950')),
+ ColorToken('content-hr', Color('#dadde1'), dark: Color('#444950')),
+ ColorToken('content-quotes', Color('#606770'), dark: Color('#a0a0a0')),
+ ColorToken(
+ 'content-quote-borders',
+ Color('#2a48df'),
+ dark: Color('#66fbd1'),
+ ),
+ ColorToken('content-captions', Color('#606770'), dark: Color('#a0a0a0')),
+ ColorToken('content-kbd', Color('#1c1e21'), dark: Color('#ffffff')),
+ ColorToken('content-kbd-shadows', Color('#1c1e21'), dark: Color('#ffffff')),
+ ColorToken('content-code', Color('#1c1e21'), dark: Color('#ffffff')),
+ ColorToken(
+ 'content-pre-code',
+ Color('#393a34'),
+ dark: Color('#e3e3e3'),
+ ),
+ ColorToken(
+ 'content-pre-bg',
+ Color('#ffffff'),
+ dark: Color('rgb(0 0 0 / 50%)'),
+ ),
+ ColorToken('content-th-borders', Color('#dadde1'), dark: Color('#444950')),
+ ColorToken('content-td-borders', Color('#dadde1'), dark: Color('#444950')),
+ // UI component tokens (non-content).
+ ColorToken('navbar-bg', Color('#fbfcff'), dark: Color('#081842')),
+ ColorToken(
+ 'primary-hover',
+ Color('#1e38b0'),
+ dark: Color('#44fac7'),
+ ),
+ ColorToken('border', Color('#dadde1'), dark: Color('#444950')),
+ ColorToken(
+ 'secondary-text',
+ Color('#606770'),
+ dark: Color('#a0a0a0'),
+ ),
+ ColorToken('surface', Color('#ffffff'), dark: Color('#1e1e1e')),
+ ],
+ typography: ContentTypography.base,
+ reset: true,
+ ),
+ ),
+ );
+}
diff --git a/site_jaspr/lib/main.server.options.dart b/site_jaspr/lib/main.server.options.dart
new file mode 100644
index 00000000..dc47b17f
--- /dev/null
+++ b/site_jaspr/lib/main.server.options.dart
@@ -0,0 +1,82 @@
+// dart format off
+// ignore_for_file: type=lint
+
+// GENERATED FILE, DO NOT MODIFY
+// Generated with jaspr_builder
+
+import 'package:jaspr/server.dart';
+import 'package:jaspr_content/components/_internal/code_block_copy_button.dart'
+ as _code_block_copy_button;
+import 'package:jaspr_content/components/_internal/zoomable_image.dart'
+ as _zoomable_image;
+import 'package:jaspr_content/components/code_block.dart' as _code_block;
+import 'package:jaspr_content/components/image.dart' as _image;
+import 'package:jaspr_content/components/sidebar_toggle_button.dart'
+ as _sidebar_toggle_button;
+import 'package:jaspr_content/components/theme_toggle.dart' as _theme_toggle;
+import 'package:site_jaspr/components/breadcrumb.dart' as _breadcrumb;
+import 'package:site_jaspr/components/collapsible_sidebar.dart'
+ as _collapsible_sidebar;
+import 'package:site_jaspr/components/doc_callout.dart' as _doc_callout;
+import 'package:site_jaspr/components/edit_page_link.dart' as _edit_page_link;
+import 'package:site_jaspr/components/icon_link.dart' as _icon_link;
+import 'package:site_jaspr/components/nav_link.dart' as _nav_link;
+import 'package:site_jaspr/components/page_navigation.dart' as _page_navigation;
+import 'package:site_jaspr/components/safe_code_block.dart' as _safe_code_block;
+import 'package:site_jaspr/components/site_footer.dart' as _site_footer;
+
+/// Default [ServerOptions] for use with your Jaspr project.
+///
+/// Use this to initialize Jaspr **before** calling [runApp].
+///
+/// Example:
+/// ```dart
+/// import 'main.server.options.dart';
+///
+/// void main() {
+/// Jaspr.initializeApp(
+/// options: defaultServerOptions,
+/// );
+///
+/// runApp(...);
+/// }
+/// ```
+ServerOptions get defaultServerOptions => ServerOptions(
+ clientId: 'main.client.dart.js',
+ clients: {
+ _code_block_copy_button.CodeBlockCopyButton:
+ ClientTarget<_code_block_copy_button.CodeBlockCopyButton>(
+ 'jaspr_content:code_block_copy_button',
+ ),
+ _zoomable_image.ZoomableImage: ClientTarget<_zoomable_image.ZoomableImage>(
+ 'jaspr_content:zoomable_image',
+ params: __zoomable_imageZoomableImage,
+ ),
+ _sidebar_toggle_button.SidebarToggleButton:
+ ClientTarget<_sidebar_toggle_button.SidebarToggleButton>(
+ 'jaspr_content:sidebar_toggle_button',
+ ),
+ _theme_toggle.ThemeToggle: ClientTarget<_theme_toggle.ThemeToggle>(
+ 'jaspr_content:theme_toggle',
+ ),
+ },
+ styles: () => [
+ ..._zoomable_image.ZoomableImage.styles,
+ ..._code_block.CodeBlock.styles,
+ ..._image.Image.styles,
+ ..._theme_toggle.ThemeToggleState.styles,
+ ..._breadcrumb.Breadcrumb.styles,
+ ..._collapsible_sidebar.CollapsibleSidebar.styles,
+ ..._doc_callout.DocCallout.styles,
+ ..._edit_page_link.EditPageLink.styles,
+ ..._icon_link.IconLink.styles,
+ ..._nav_link.NavLink.styles,
+ ..._page_navigation.PageNavigation.styles,
+ ..._safe_code_block.SafeCodeBlock.styles,
+ ..._site_footer.SiteFooter.styles,
+ ],
+);
+
+Map __zoomable_imageZoomableImage(
+ _zoomable_image.ZoomableImage c,
+) => {'src': c.src, 'alt': c.alt, 'caption': c.caption};
diff --git a/site_jaspr/lib/styles/site_styles.dart b/site_jaspr/lib/styles/site_styles.dart
new file mode 100644
index 00000000..f223013b
--- /dev/null
+++ b/site_jaspr/lib/styles/site_styles.dart
@@ -0,0 +1,759 @@
+import 'package:jaspr/dom.dart';
+
+/// Comprehensive site styles matching the original Docusaurus/Infima design.
+///
+/// This file provides ALL visual overrides in one place, replacing the scattered
+/// CSS rules that were previously in `_fontStyles` in `site_footer.dart`.
+///
+/// Color reference (from `site/src/css/custom.css`):
+///
+/// | Variable | Light | Dark |
+/// |-----------------------|-----------|-----------|
+/// | primary | #2a48df | #66fbd1 |
+/// | primary-dark (hover) | #1f3ccf | #44fac7 |
+/// | background | #fbfcff | #020f30 |
+/// | navbar-bg | #fbfcff | #081842 |
+/// | code-bg (inline dark) | β | #081842 |
+
+/// Global site styles injected into `` via [Document.head].
+List get siteStyles => [
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 1. FONT & BASE
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css(':root').styles(
+ raw: {
+ '--content-font':
+ "'Poppins', ui-sans-serif, system-ui, sans-serif, "
+ "'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', "
+ "'Noto Color Emoji'",
+ // Infima monospace font stack
+ '--ifm-font-family-monospace':
+ "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', "
+ "'Courier New', monospace",
+ },
+ ),
+ css('html, body').styles(
+ raw: {
+ 'font-family': 'var(--content-font)',
+ 'line-height': '1.65',
+ },
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 2. TYPOGRAPHY OVERRIDES
+ // Match Docusaurus/Infima heading sizes and spacing exactly.
+ // jaspr_content's ContentTypography uses smaller headings (h1=2.25em,
+ // h2=1.5em) and different line-height (1.75). Override to match Infima.
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // Heading sizes: Infima uses h1=3rem, h2=2rem, h3=1.5rem, h4=1.25rem
+ // Heading anchors: framework uses display:flex on headings which makes
+ // the # anchor a block flex-child on a new line. Force inline so it
+ // doesn't add extra height.
+ css('.content :is(h1, h2, h3, h4, h5, h6)[anchor] > a').styles(
+ raw: {'position': 'absolute'},
+ ),
+ css('.content h1').styles(
+ fontSize: 3.rem,
+ fontWeight: FontWeight.w700,
+ lineHeight: 1.25.em,
+ raw: {'margin': '0 0 1.5625rem'},
+ ),
+ css('.content h2').styles(
+ fontSize: 2.rem,
+ fontWeight: FontWeight.w700,
+ raw: {'margin': '2.5rem 0 1.25rem', 'line-height': '1.25'},
+ ),
+ css('.content h3').styles(
+ fontSize: 1.5.rem,
+ fontWeight: FontWeight.w700,
+ raw: {'margin': '1.875rem 0 1.25rem', 'line-height': '1.25'},
+ ),
+ css('.content h4').styles(
+ fontSize: 1.25.rem,
+ lineHeight: 1.5.em,
+ raw: {'margin': '1.5rem 0 0.5rem'},
+ ),
+ // Content line-height: Infima uses 1.65 (not jaspr_content's 1.75)
+ css('.content').styles(raw: {'line-height': '1.65'}),
+ // Paragraphs: Docusaurus uses bottom-only margin (1.25rem); Jaspr default is top+bottom
+ css('.content p').styles(raw: {'margin': '0 0 1.25rem'}),
+ // Lists: match Docusaurus/Infima spacing (bottom-only margin, left padding)
+ css('.content ul, .content ol').styles(
+ margin: Margin.only(top: Unit.zero, bottom: 1.25.rem),
+ raw: {'padding-left': '2rem'},
+ ),
+ css('.content li').styles(
+ padding: Padding.zero,
+ margin: Margin.zero,
+ ),
+ // Remove backtick pseudo-elements and quote marks (Docusaurus doesn't have these)
+ css('.content code::before').styles(raw: {'content': 'none'}),
+ css('.content code::after').styles(raw: {'content': 'none'}),
+ css('.content blockquote p:first-of-type::before').styles(
+ raw: {'content': 'none'},
+ ),
+ css('.content blockquote p:last-of-type::after').styles(
+ raw: {'content': 'none'},
+ ),
+ // Inline code: light gray background, smaller font (matching Infima)
+ // Use :not(pre) > code to avoid styling code inside pre blocks
+ css('.content :not(pre) > code').styles(
+ padding: Padding.all(0.1.rem),
+ border: Border.all(color: Color('rgba(0, 0, 0, 0.1)'), width: 1.px),
+ radius: BorderRadius.circular(0.4.rem),
+ fontSize: Unit.percent(95),
+ raw: {
+ 'background-color': '#f6f7f8',
+ 'font-family': 'var(--ifm-font-family-monospace)',
+ 'font-weight': 'inherit',
+ },
+ ),
+ // Pre/code blocks: match Docusaurus sizing (15.2px font, 22px line-height)
+ // Docusaurus: pre has no padding/margin, code has 16px padding.
+ // Jaspr base: pre has padding+margin from ContentTypography. Override to match.
+ css('.content pre').styles(
+ padding: Padding.zero,
+ fontSize: 0.95.rem,
+ raw: {
+ 'font-family': 'var(--ifm-font-family-monospace)',
+ 'line-height': '1.45',
+ 'margin': '0 0 1.25rem',
+ },
+ ),
+ css('.content pre code').styles(
+ padding: Padding.all(1.rem),
+ fontSize: Unit.percent(100),
+ raw: {
+ 'font-family': 'var(--ifm-font-family-monospace)',
+ 'background': 'transparent',
+ 'border-radius': '0',
+ 'display': 'block',
+ 'line-height': 'inherit',
+ },
+ ),
+ // Code block copy button: match Docusaurus style.
+ // Framework sets opacity:0 and position:absolute AFTER our styles,
+ // so we use !important to override.
+ css('.code-block button').styles(
+ display: Display.flex,
+ padding: Padding.all(0.25.rem),
+ border: Border.all(color: Color('var(--border)'), width: 1.px),
+ radius: BorderRadius.circular(0.4.rem),
+ justifyContent: JustifyContent.center,
+ alignItems: AlignItems.center,
+ backgroundColor: Color('var(--surface)'),
+ raw: {
+ 'position': 'absolute !important',
+ 'top': '0.5rem',
+ 'right': '0.5rem',
+ 'width': 'auto !important',
+ 'height': 'auto !important',
+ 'opacity': '0 !important',
+ 'color': 'var(--secondary-text) !important',
+ 'cursor': 'pointer',
+ 'transition': 'opacity 0.2s, color 0.2s',
+ },
+ ),
+ css('.code-block button svg').styles(
+ raw: {'width': '18px !important', 'height': '18px !important'},
+ ),
+ css('.code-block:hover button').styles(
+ raw: {'opacity': '0.5 !important'},
+ ),
+ css('.code-block button:hover').styles(
+ raw: {'opacity': '1 !important', 'color': 'var(--text) !important'},
+ ),
+ // Green check icon after successful copy (CheckIcon has no , CopyIcon does).
+ // Needs both light and dark mode rules to beat specificity of dark hover rule.
+ css('.code-block button:not(:has(svg rect))').styles(
+ raw: {'color': '#00a86b !important', 'opacity': '1 !important'},
+ ),
+ css('.code-block button:not(:has(svg rect)):hover').styles(
+ raw: {'color': '#00a86b !important'},
+ ),
+ css('[data-theme="dark"] .code-block button:not(:has(svg rect))').styles(
+ raw: {'color': '#00a86b !important', 'opacity': '1 !important'},
+ ),
+ css('[data-theme="dark"] .code-block button:not(:has(svg rect)):hover').styles(
+ raw: {'color': '#00a86b !important'},
+ ),
+ // Content links: primary color, no underline by default (matching original)
+ css('.content a:not(.workflow-card):not(.page-nav-prev):not(.page-nav-next)').styles(
+ textDecoration: TextDecoration.none,
+ ),
+ css('.content a:not(.workflow-card):not(.page-nav-prev):not(.page-nav-next):hover').styles(
+ textDecoration: TextDecoration(line: TextDecorationLine.underline),
+ ),
+ // Blockquote: no italic, normal weight (matching Infima defaults)
+ css('.content blockquote').styles(
+ fontWeight: FontWeight.normal,
+ fontStyle: FontStyle.normal,
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 3. HEADER / NAVBAR
+ // Docusaurus navbar: 60px height (3.75rem), padding 8px 16px
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // Opaque header background for both modes
+ css('.docs .header-container').styles(
+ backgroundColor: Color('var(--navbar-bg)'),
+ raw: {'z-index': '20'},
+ ),
+ // Match Docusaurus navbar: 60px height, box-shadow (not border-bottom)
+ css('.header').styles(
+ height: 3.75.rem,
+ padding: Padding.symmetric(horizontal: 1.rem, vertical: 0.5.rem),
+ border: Border.none,
+ raw: {'box-shadow': 'rgba(0, 0, 0, 0.1) 0px 1px 2px 0px'},
+ ),
+ // Hide the title text in the header (original only shows the logo badge)
+ css('.header-title > span').styles(display: Display.none),
+ // Hide the duplicate title, description, and image rendered from frontmatter
+ css('.content-header').styles(display: Display.none),
+ // Larger logo badge in the header (matching Docusaurus 32px)
+ css('.header .header-title img').styles(height: 2.rem),
+ // Vertically center nav links with icons in the header
+ css('.header .header-items').styles(alignItems: AlignItems.center),
+ // Hamburger menu button: match Docusaurus 30Γ30 SVG (jaspr_content default is 20Γ20)
+ css('.sidebar-toggle-button svg').styles(
+ width: 30.px,
+ height: 30.px,
+ ),
+ // Hide header nav items on narrow viewports
+ css.media(MediaQuery.all(maxWidth: 1000.px), [
+ css('.header .header-items').styles(display: Display.none),
+ ]),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 4. DARK MODE ICON SWITCHING
+ // Show .icon-light in light mode, .icon-dark in dark mode.
+ // .sidebar-icon-* are used in sidebar primary-panel icon links.
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('[data-theme="dark"] .icon-link .icon-light').styles(
+ display: Display.none,
+ ),
+ css('[data-theme="dark"] .icon-link .icon-dark').styles(
+ raw: {'display': 'flex !important'},
+ ),
+ // Sidebar primary-panel icon switching (sidebar-icon-light / sidebar-icon-dark)
+ css('.sidebar-icon-dark').styles(display: Display.none),
+ css('[data-theme="dark"] .sidebar-icon-light').styles(display: Display.none),
+ css('[data-theme="dark"] .sidebar-icon-dark').styles(
+ raw: {'display': 'inline !important'},
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 5. DARK MODE "GET STARTED" BUTTON
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('[data-theme="dark"] .nav-button').styles(
+ color: Color('var(--background)'),
+ ),
+ // Nav links (e.g. "VGV Dev Tools"): lighter weight, slightly smaller
+ css('.nav-link:not(.nav-button)').styles(
+ fontSize: 0.975.rem,
+ fontWeight: FontWeight.w400,
+ ),
+ // Dark mode: nav links should be white
+ css('[data-theme="dark"] .nav-link:not(.nav-button)').styles(color: Colors.white),
+ // Dark mode mobile: "Get Started" becomes a plain transparent link, matching
+ // Docusaurus custom.css html[data-theme='dark'] @media (max-width:996px) rule.
+ css.media(MediaQuery.all(maxWidth: 996.px), [
+ css('[data-theme="dark"] .nav-button').styles(
+ raw: {'background-color': 'transparent', 'color': '#ffffff'},
+ ),
+ ]),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 5b. BREADCRUMB COLORS
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // Light mode: breadcrumb links match Docusaurus body text (#1c1e21)
+ css('.breadcrumb-link').styles(color: Color('var(--text)')),
+ // Dark mode: separator needs explicit override (--secondary-text dark β #e3e3e3)
+ css('[data-theme="dark"] .breadcrumb-sep').styles(
+ color: Color('var(--text)'),
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 6. THEME TOGGLE
+ // Reverse the icons: show current-mode icon instead of target-mode.
+ // Light mode: hide moon (first span), show sun (last span).
+ // Dark mode: show moon (first span), hide sun (last span).
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('.theme-toggle').styles(
+ padding: Padding.zero,
+ justifyContent: JustifyContent.center,
+ alignItems: AlignItems.center,
+ raw: {
+ 'width': '32px',
+ 'height': '32px',
+ 'border-radius': '50% !important',
+ 'box-sizing': 'border-box',
+ 'cursor': 'pointer',
+ },
+ ),
+ css('.theme-toggle:hover').styles(
+ backgroundColor: Color('rgba(0, 0, 0, 0.1)'),
+ ),
+ css('[data-theme="dark"] .theme-toggle:hover').styles(
+ backgroundColor: Color('rgba(255, 255, 255, 0.1)'),
+ ),
+ css('.theme-toggle svg').styles(
+ raw: {'width': '24px', 'height': '24px'},
+ ),
+ // Reverse icons: show current state (sun in light, moon in dark).
+ // Framework default: moon in light, sun in dark.
+ css('[data-theme="light"] .theme-toggle > span:first-child').styles(
+ raw: {'display': 'none !important'},
+ ),
+ css('[data-theme="light"] .theme-toggle > span:last-child').styles(
+ raw: {'display': 'inline !important'},
+ ),
+ css('[data-theme="dark"] .theme-toggle > span:first-child').styles(
+ raw: {'display': 'inline !important'},
+ ),
+ css('[data-theme="dark"] .theme-toggle > span:last-child').styles(
+ raw: {'display': 'none !important'},
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 7. DOCS LAYOUT SPACING & BREADCRUMB
+ // Docusaurus: sticky navbar 60px + 16px gap = content at 76px.
+ // Jaspr default: fixed header 60px + main padding-top 4rem + div
+ // padding-top 2rem = 96px. Reduce to match Docusaurus.
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // Reduce main padding-top to exactly header height
+ css('.docs .main-container main').styles(
+ padding: Padding.only(top: 3.75.rem),
+ ),
+ // Reduce inner div padding to match Docusaurus 16px gap
+ // max-width 1320px matches Docusaurus .container constraint
+ css('.docs .main-container main > div').styles(
+ padding: Padding.only(top: 1.rem, left: 1.rem, right: 1.rem),
+ raw: {'max-width': '1320px', 'margin': '0 auto'},
+ ),
+ // Breadcrumb: position above content using flexbox order
+ css('.content-container').styles(
+ display: Display.flex,
+ flexDirection: FlexDirection.column,
+ ),
+ css('.content-footer').styles(raw: {'display': 'contents'}),
+ css('.breadcrumb').styles(raw: {'order': '-1'}),
+ // Breadcrumb: match Docusaurus sizing (12.8px font, link padding, bottom margin)
+ css('.docs .breadcrumb').styles(
+ padding: Padding.zero,
+ margin: Margin.only(bottom: 12.8.px),
+ fontSize: 12.8.px,
+ ),
+ css('.docs .breadcrumb-link, .docs .breadcrumb-current').styles(
+ padding: Padding.symmetric(vertical: 5.12.px, horizontal: 10.24.px),
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 8. SIDEBAR β layout/sizing only.
+ // Link/hover/active styles live in CollapsibleSidebar.styles using
+ // var(--primary) (theme-aware). Width matches Docusaurus 300px.
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // Sidebar width: match Docusaurus 300px (--doc-sidebar-width)
+ // Use high specificity to override DocsLayout's .docs .main-container .sidebar-container
+ css('.docs .main-container .sidebar-container').styles(
+ width: 300.px,
+ padding: Padding.only(bottom: 8.rem),
+ raw: {
+ 'overflow-y': 'auto',
+ 'overflow-x': 'hidden',
+ 'scrollbar-width': 'none',
+ '-ms-overflow-style': 'none',
+ },
+ ),
+ css('.docs .main-container .sidebar-container::-webkit-scrollbar').styles(
+ display: Display.none,
+ ),
+ css('.docs .sidebar').styles(width: 300.px, raw: {'overflow-x': 'hidden'}),
+ // Sidebar border-right: match Docusaurus 1px solid border
+ css('.docs .sidebar-container').styles(
+ border: Border.only(
+ right: BorderSide(width: 1.px, color: Color('var(--border)')),
+ ),
+ ),
+ // Dark mode sidebar links: Docusaurus uses #dadde1
+ css('[data-theme="dark"] .docs .sidebar a').styles(
+ color: Color('#dadde1'),
+ ),
+ // Adjust main padding-left to match wider sidebar (desktop only)
+ css.media(MediaQuery.all(minWidth: 1024.px), [
+ css('.docs .main-container main').styles(
+ padding: Padding.only(left: 300.px),
+ ),
+ ]),
+ // Desktop: align sidebar top with bottom of 3.75rem header.
+ // DocsLayout defaults to top: 4rem β override to close the 4px gap.
+ css.media(MediaQuery.all(minWidth: 1024.px), [
+ css('.docs .main-container .sidebar-container').styles(
+ raw: {'top': '3.75rem'},
+ ),
+ ]),
+ // Mobile: 83 vw width (matches Docusaurus --ifm-navbar-sidebar-width) + z-index
+ // above the fixed header (z-index 10) so the panel slides over the navbar.
+ // .sidebar must also be 100% so nav items fill the panel (not the 300px
+ // desktop value which would leave dead space on wider phones).
+ css.media(MediaQuery.all(maxWidth: 1023.px), [
+ css('.docs .main-container .sidebar-container').styles(
+ zIndex: ZIndex(200),
+ raw: {'width': '83vw'},
+ ),
+ css('.docs .sidebar').styles(width: Unit.percent(100)),
+ ]),
+ // Mobile sidebar: Workflows image-button (the SVG pill used in the header).
+ css('.mobile-workflows-btn').styles(
+ display: Display.flex,
+ alignItems: AlignItems.center,
+ textDecoration: TextDecoration.none,
+ ),
+ css('.mobile-workflows-btn:hover').styles(opacity: 0.8),
+
+ // Dark mode mobile: sidebar panel uses the navbar background (#081842),
+ // matching Docusaurus where --ifm-navbar-background-color is applied to
+ // the mobile overlay instead of the page background (#020f30).
+ css.media(MediaQuery.all(maxWidth: 1023.px), [
+ css('[data-theme="dark"] .docs .main-container .sidebar-container').styles(
+ backgroundColor: Color('var(--navbar-bg)'),
+ ),
+ ]),
+ // Fix DocsLayout's sidebar-barrier:
+ // β’ position:fixed so it covers the full viewport (including the header).
+ // β’ background:#000 so that at DocsLayout's opacity:0.5 it reads as
+ // rgba(0,0,0,0.5) β the standard Docusaurus dark scrim.
+ css('.docs .main-container .sidebar-barrier').styles(
+ raw: {'position': 'fixed', 'inset': '0', 'background': '#000'},
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 9. CONTENT AREA LINKS
+ // Primary color, no underline; underline on hover (matching original).
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('.content-container a:not(.breadcrumb-link):not(.workflow-card):not(.page-nav-prev):not(.page-nav-next)').styles(
+ color: Color('var(--content-links)'),
+ textDecoration: TextDecoration.none,
+ ),
+ css(
+ '.content-container a:not(.breadcrumb-link):not(.workflow-card):not(.page-nav-prev):not(.page-nav-next):hover',
+ ).styles(
+ textDecoration: TextDecoration(line: TextDecorationLine.underline),
+ ),
+ // Callout links: inherit foreground color + always underlined (Infima alert behavior).
+ // Overrides the blue `.content-container a` rule above via cascade order.
+ css('.content-container .doc-callout a').styles(
+ color: Color.inherit,
+ fontWeight: FontWeight.w400,
+ textDecoration: TextDecoration(line: TextDecorationLine.underline),
+ ),
+ css('.content-container .doc-callout a:hover').styles(
+ raw: {'text-decoration-thickness': '2px'},
+ ),
+ // Underline color matches the left border color per type.
+ css('.content-container .doc-callout-info a').styles(
+ raw: {'text-decoration-color': '#54c7ec'},
+ ),
+ css('.content-container .doc-callout-warning a').styles(
+ raw: {'text-decoration-color': '#e6a700'},
+ ),
+ css('.content-container .doc-callout-error a').styles(
+ raw: {'text-decoration-color': '#fa5252'},
+ ),
+ css('.content-container .doc-callout-success a').styles(
+ raw: {'text-decoration-color': '#00a400'},
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 10. DARK MODE: CODE BLOCKS & SCROLLBARS
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // Dark mode: all code (inline + blocks) uses navy background
+ // Matches Docusaurus custom.css: html[data-theme='dark'] code { background: #081842 }
+ css('[data-theme="dark"] code').styles(
+ raw: {'background': '#081842 !important'},
+ ),
+ // Dark mode inline code: match Docusaurus border and text color
+ css('[data-theme="dark"] .content :not(pre) > code').styles(
+ border: Border.all(color: Color('rgba(0, 0, 0, 0.1)'), width: 1.px),
+ color: Color('#e3e3e3'),
+ ),
+ css('[data-theme="dark"]').styles(
+ raw: {'scrollbar-color': '#ffffff30 transparent'},
+ ),
+ // Dark mode headings: Docusaurus uses #e3e3e3, not pure white
+ css('[data-theme="dark"] .content h1, [data-theme="dark"] .content h2, [data-theme="dark"] .content h3').styles(
+ color: Color('#e3e3e3'),
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 11. FOOTER & PAGE NAVIGATION SPACING
+ // Docusaurus: footer margin-top 64px, pagination margin-top 48px.
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('.site-footer').styles(margin: Margin.only(top: 4.rem)),
+ // page-nav spacing is set in PageNavigation.styles (48px margin-top, zero padding)
+ // Dark mode: page nav prev/next blocks
+ css('[data-theme="dark"] .page-nav-prev, [data-theme="dark"] .page-nav-next').styles(
+ border: Border.all(color: Color('#606770'), width: 1.px),
+ ),
+ css('[data-theme="dark"] .page-nav-label').styles(
+ color: Colors.white,
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 12. TABLE OF CONTENTS & CONTENT WIDTH
+ // Docusaurus: 213px TOC, 12.8px font, 703px content width.
+ // Jaspr default: 272px TOC, extra padding β only 588px content.
+ // Fix TOC width and reduce padding to match Docusaurus layout.
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // TOC: hide "On this page" heading (Docusaurus doesn't show it)
+ css('.docs .main-container main > div aside.toc h3').styles(
+ display: Display.none,
+ ),
+ // Show TOC at >=1000px (DocsLayout default is 1280px)
+ css.media(MediaQuery.all(minWidth: 1000.px), [
+ css('.docs .main-container main > div aside.toc').styles(
+ display: Display.block,
+ ),
+ ]),
+ // TOC: match Docusaurus styling (left border, link colors, sub-item pills)
+ css('.docs .main-container main > div aside.toc').styles(
+ raw: {'min-width': '140px', 'flex': '0 1 25%'},
+ ),
+ css('.docs .main-container main > div aside.toc > div').styles(
+ padding: Padding.only(left: 0.5.rem),
+ border: Border.only(
+ left: BorderSide(width: 1.px, color: Color('var(--border)')),
+ ),
+ ),
+ css('.docs .main-container main > div aside.toc li').styles(
+ fontSize: 12.8.px,
+ ),
+ // TOC links: gray by default, blue when active (matching Docusaurus)
+ css('.toc a').styles(
+ color: Color('var(--secondary-text)'),
+ textDecoration: TextDecoration.none,
+ ),
+ css('.toc a:hover').styles(
+ color: Color('var(--primary)'),
+ ),
+ css('.toc a.toc-active').styles(
+ color: Color('var(--primary)'),
+ fontWeight: FontWeight.w500,
+ ),
+ // TOC code sub-items (backtick headings): rounded pill border
+ css('.toc a code').styles(
+ padding: Padding.symmetric(horizontal: 0.5.rem, vertical: 0.125.rem),
+ border: Border.all(color: Color('var(--border)'), width: 1.px),
+ radius: BorderRadius.circular(12.px),
+ fontSize: 11.px,
+ raw: {'font-family': 'inherit'},
+ ),
+ // Dark mode TOC: override link color (--secondary-text dark is #a0a0a0, need white)
+ css('[data-theme="dark"] .toc a').styles(
+ color: Colors.white,
+ ),
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 12b. MOBILE TOC (collapsible "On this page" above content < 1000px)
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('.mobile-toc', [
+ css('&').styles(
+ margin: Margin.only(bottom: 1.rem),
+ radius: BorderRadius.circular(0.4.rem),
+ raw: {
+ 'background': 'rgba(0, 0, 0, 0.05)',
+ },
+ ),
+ ]),
+ // Hide mobile TOC at wide viewports where sidebar TOC is visible
+ css.media(MediaQuery.all(minWidth: 1000.px), [
+ css('.mobile-toc').styles(display: Display.none),
+ ]),
+ css('.mobile-toc-toggle', [
+ css('&').styles(
+ display: Display.flex,
+ width: Unit.percent(100),
+ padding: Padding.symmetric(horizontal: 1.rem, vertical: 0.5.rem),
+ cursor: Cursor.pointer,
+ fontSize: 0.875.rem,
+ fontWeight: FontWeight.w700,
+ raw: {
+ 'align-items': 'center',
+ 'justify-content': 'space-between',
+ 'background': 'none',
+ 'border': 'none',
+ 'color': 'inherit',
+ 'font-family': 'inherit',
+ },
+ ),
+ css('svg').styles(
+ opacity: 0.5,
+ raw: {
+ 'transition': 'transform 0.2s ease',
+ },
+ ),
+ ]),
+ css('.mobile-toc.expanded .mobile-toc-toggle svg').styles(
+ raw: {'transform': 'rotate(180deg)'},
+ ),
+ css('.mobile-toc-content', [
+ css('&').styles(
+ overflow: Overflow.hidden,
+ raw: {
+ 'max-height': '0',
+ 'transition': 'max-height 0.3s ease',
+ },
+ ),
+ css('ul').styles(
+ padding: Padding.only(
+ left: 1.rem,
+ right: 1.rem,
+ bottom: 0.75.rem,
+ ),
+ margin: Margin.zero,
+ listStyle: ListStyle.none,
+ ),
+ css('li').styles(
+ padding: Padding.symmetric(vertical: 0.25.rem),
+ ),
+ ]),
+ // Mobile TOC code sub-items: pill border (same as sidebar TOC)
+ css('.mobile-toc .mobile-toc-content a code').styles(
+ padding: Padding.symmetric(horizontal: 0.5.rem, vertical: 0.125.rem),
+ border: Border.all(color: Color('var(--border)'), width: 1.px),
+ radius: BorderRadius.circular(12.px),
+ fontSize: 11.px,
+ raw: {'font-family': 'inherit'},
+ ),
+ // Mobile TOC links: use higher specificity to override .content-container a
+ css('.mobile-toc .mobile-toc-content a').styles(
+ color: Color('var(--text)'),
+ fontSize: 0.8125.rem,
+ textDecoration: TextDecoration.none,
+ ),
+ css('.mobile-toc .mobile-toc-content a:hover').styles(
+ color: Color('var(--primary)'),
+ ),
+ css('.mobile-toc.expanded .mobile-toc-content').styles(
+ raw: {'max-height': '500px'},
+ ),
+ // Dark mode mobile TOC
+ css('[data-theme="dark"] .mobile-toc').styles(
+ raw: {
+ 'background': 'rgba(255, 255, 255, 0.05)',
+ },
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 12c. TABLES (matching Docusaurus/Infima table styling)
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('.content-container table').styles(
+ width: Unit.percent(100),
+ raw: {'display': 'table', 'border-collapse': 'collapse', 'margin-bottom': '1.25rem'},
+ ),
+ css('.content-container th, .content-container td').styles(
+ padding: Padding.all(0.75.rem),
+ border: Border.all(color: Color('var(--border)'), width: 1.px),
+ ),
+ css('.content-container thead tr').styles(
+ raw: {'background': '#f6f7f8'},
+ ),
+ css('.content-container th').styles(
+ fontWeight: FontWeight.w700,
+ ),
+ css('.content-container tr:nth-child(2n)').styles(
+ raw: {'background': '#fbfcfd'},
+ ),
+ // Dark mode tables
+ css('[data-theme="dark"] .content-container thead tr').styles(
+ raw: {'background': 'rgba(255, 255, 255, 0.05)'},
+ ),
+ css('[data-theme="dark"] .content-container tr:nth-child(2n)').styles(
+ raw: {'background': 'rgba(255, 255, 255, 0.02)'},
+ ),
+
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ // 13. WORKFLOW CARDS
+ // Docusaurus category page: 2-column card grid with icon, title,
+ // and truncated description. Matches /docs/category/workflows.
+ // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ css('.workflow-cards', [
+ css('&').styles(
+ display: Display.grid,
+ gap: Gap(column: 2.rem, row: 2.rem),
+ raw: {'grid-template-columns': '1fr', 'margin': '1.5rem 0'},
+ ),
+ ]),
+ css.media(MediaQuery.all(minWidth: 1024.px), [
+ css('.workflow-cards').styles(
+ raw: {'grid-template-columns': 'repeat(2, 1fr)'},
+ ),
+ ]),
+ css('.workflow-card', [
+ css('&').styles(
+ display: Display.block,
+ padding: Padding.all(2.rem),
+ radius: BorderRadius.circular(0.8.rem),
+ textDecoration: TextDecoration.none,
+ raw: {
+ 'border': '1px solid #ebedf0',
+ 'background': '#fbfcff',
+ 'color': 'inherit',
+ 'transition': 'border-color 0.2s ease',
+ 'box-shadow': 'rgba(0, 0, 0, 0.15) 0px 1.5px 3px 0px',
+ },
+ ),
+ css('&:hover').styles(
+ textDecoration: TextDecoration.none,
+ raw: {'border-color': 'var(--primary)'},
+ ),
+ css('h3').styles(
+ margin: Margin.only(bottom: 1.rem),
+ fontSize: 1.2.rem,
+ fontWeight: FontWeight.w700,
+ textDecoration: TextDecoration.none,
+ raw: {'margin-top': '0'},
+ ),
+ css('p').styles(
+ margin: Margin.zero,
+ overflow: Overflow.hidden,
+ color: Color('#444950'),
+ fontSize: 0.8.rem,
+ textDecoration: TextDecoration.none,
+ raw: {
+ 'display': '-webkit-box',
+ '-webkit-line-clamp': '1',
+ '-webkit-box-orient': 'vertical',
+ },
+ ),
+ ]),
+ // Dark mode workflow cards
+ css('[data-theme="dark"] .workflow-card').styles(
+ raw: {
+ 'background': '#314155',
+ 'border-color': '#444950',
+ },
+ ),
+ css('[data-theme="dark"] .workflow-card p').styles(
+ color: Color('#ebedf0'),
+ ),
+
+ // Remove main-container side padding (jaspr_content adds 1.25rem at 768px+)
+ // Docusaurus has no container-level padding β padding lives inside content column
+ css.media(MediaQuery.all(minWidth: 768.px), [
+ css('.docs .main-container').styles(
+ padding: Padding.symmetric(horizontal: Unit.zero),
+ ),
+ ]),
+ // Desktop content layout: remove inner div horizontal padding and move it
+ // to content-container, so 75% is calculated on the full available space
+ // (matching Docusaurus where contentCol = 75% of docMainContainer width)
+ css.media(MediaQuery.all(minWidth: 1000.px), [
+ css('.docs .main-container main > div').styles(
+ padding: Padding.only(top: 1.rem, left: Unit.zero, right: Unit.zero),
+ ),
+ css('.docs .main-container main > div .content-container').styles(
+ padding: Padding.symmetric(horizontal: 1.rem),
+ raw: {'max-width': '75%'},
+ ),
+ ]),
+];
diff --git a/site_jaspr/pubspec.lock b/site_jaspr/pubspec.lock
new file mode 100644
index 00000000..cdc0d15b
--- /dev/null
+++ b/site_jaspr/pubspec.lock
@@ -0,0 +1,693 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ _fe_analyzer_shared:
+ dependency: transitive
+ description:
+ name: _fe_analyzer_shared
+ sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "92.0.0"
+ analyzer:
+ dependency: transitive
+ description:
+ name: analyzer
+ sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.0.0"
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.9"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.7.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.13.0"
+ bazel_worker:
+ dependency: transitive
+ description:
+ name: bazel_worker
+ sha256: "87cae9150fcf9942b8057e5f51c4848a3efde1289e97411e1c8f01e350120999"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.5"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ build:
+ dependency: transitive
+ description:
+ name: build
+ sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.4"
+ build_config:
+ dependency: transitive
+ description:
+ name: build_config
+ sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ build_daemon:
+ dependency: transitive
+ description:
+ name: build_daemon
+ sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.1"
+ build_modules:
+ dependency: transitive
+ description:
+ name: build_modules
+ sha256: "2e30f22597d45fd49058bb29f47e947d3a045db2a72d3fec27a064cb2164337e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.7"
+ build_runner:
+ dependency: "direct dev"
+ description:
+ name: build_runner
+ sha256: "39ad4ca8a2876779737c60e4228b4bcd35d4352ef7e14e47514093edc012c734"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.11.1"
+ build_web_compilers:
+ dependency: "direct dev"
+ description:
+ name: build_web_compilers
+ sha256: "7975a0304abc80dfaaf9897047ae85d78753ec6b8b97ad113d428464dd17145e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.4.13"
+ built_collection:
+ dependency: transitive
+ description:
+ name: built_collection
+ sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.1"
+ built_value:
+ dependency: transitive
+ description:
+ name: built_value
+ sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9"
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.12.4"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.1"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.4"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.2"
+ code_builder:
+ dependency: transitive
+ description:
+ name: code_builder
+ sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.11.1"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.19.1"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.7"
+ csslib:
+ dependency: transitive
+ description:
+ name: csslib
+ sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.2"
+ dart_style:
+ dependency: transitive
+ description:
+ name: dart_style
+ sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.3"
+ fbh_front_matter:
+ dependency: transitive
+ description:
+ name: fbh_front_matter
+ sha256: "18b2f355326ff2b7ebd64eb1d969c091895ffa755ab55ede5de68f59cf115024"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.0.1"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ gato:
+ dependency: transitive
+ description:
+ name: gato
+ sha256: "03fa3a33d50f71a912e30827ddf47c89e06e9acfd2b6b178aff0e1aa2f1e8b90"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.0.5+1"
+ glob:
+ dependency: transitive
+ description:
+ name: glob
+ sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.3"
+ graphs:
+ dependency: transitive
+ description:
+ name: graphs
+ sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.2"
+ hotreloader:
+ dependency: transitive
+ description:
+ name: hotreloader
+ sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.3.0"
+ html:
+ dependency: transitive
+ description:
+ name: html
+ sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.15.6"
+ html_unescape:
+ dependency: transitive
+ description:
+ name: html_unescape
+ sha256: "15362d7a18f19d7b742ef8dcb811f5fd2a2df98db9f80ea393c075189e0b61e3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.6.0"
+ http_multi_server:
+ dependency: transitive
+ description:
+ name: http_multi_server
+ sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.2"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.2"
+ intl:
+ dependency: transitive
+ description:
+ name: intl
+ sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.20.2"
+ io:
+ dependency: transitive
+ description:
+ name: io
+ sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.5"
+ jaspr:
+ dependency: "direct main"
+ description:
+ name: jaspr
+ sha256: "1ef6f6bf5996209a8e2e650159c3554c07ad77613f5db2174d4f47b6204b6a19"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.22.3"
+ jaspr_builder:
+ dependency: "direct dev"
+ description:
+ name: jaspr_builder
+ sha256: "77d777d7bbd0ec795dee9d16a68c38c659d7b3b9a00992b857c2061954ae8a79"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.22.3"
+ jaspr_content:
+ dependency: "direct main"
+ description:
+ name: jaspr_content
+ sha256: e2fa5e7252f67894fc8ff08791f35780b91654f9f24f79a2d47a8b1fa71411ca
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.5.0"
+ jaspr_router:
+ dependency: "direct main"
+ description:
+ name: jaspr_router
+ sha256: a82899af7e1717ef1643889a00a9f1a738983b58a7db13f1512a0066056d6a1e
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.1"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.11.0"
+ lints:
+ dependency: "direct dev"
+ description:
+ name: lints
+ sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.1"
+ liquify:
+ dependency: transitive
+ description:
+ name: liquify
+ sha256: "23aaa728dd2adc15dcc863772dec64425e8bc35bfa3f81323e0726fb83e1e28f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.1"
+ logging:
+ dependency: transitive
+ description:
+ name: logging
+ sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ markdown:
+ dependency: transitive
+ description:
+ name: markdown
+ sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.3.0"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.19"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: "9f29b9bcc8ee287b1a31e0d01be0eae99a930dbffdaecf04b3f3d82a969f296f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.18.1"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ mustache_template:
+ dependency: transitive
+ description:
+ name: mustache_template
+ sha256: "4326d0002ff58c74b9486990ccbdab08157fca3c996fe9e197aff9d61badf307"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.3"
+ package_config:
+ dependency: transitive
+ description:
+ name: package_config
+ sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.1"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.2"
+ pool:
+ dependency: transitive
+ description:
+ name: pool
+ sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.2"
+ posix:
+ dependency: transitive
+ description:
+ name: posix
+ sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.5.0"
+ protobuf:
+ dependency: transitive
+ description:
+ name: protobuf
+ sha256: "75ec242d22e950bdcc79ee38dd520ce4ee0bc491d7fadc4ea47694604d22bf06"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.0"
+ pub_semver:
+ dependency: transitive
+ description:
+ name: pub_semver
+ sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ pubspec_parse:
+ dependency: transitive
+ description:
+ name: pubspec_parse
+ sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.0"
+ scratch_space:
+ dependency: transitive
+ description:
+ name: scratch_space
+ sha256: "3417e014d20b12cebc5bfb1c0b1f63806054177158596cc31cc4d9aaca767a60"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ shelf:
+ dependency: transitive
+ description:
+ name: shelf
+ sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.2"
+ shelf_gzip:
+ dependency: transitive
+ description:
+ name: shelf_gzip
+ sha256: "4f4b793c0f969f348aece1ab4cc05fceba9fea431c1ce76b1bc0fa369cecfc15"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.0"
+ shelf_proxy:
+ dependency: transitive
+ description:
+ name: shelf_proxy
+ sha256: a71d2307f4393211930c590c3d2c00630f6c5a7a77edc1ef6436dfd85a6a7ee3
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ shelf_static:
+ dependency: transitive
+ description:
+ name: shelf_static
+ sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.3"
+ shelf_web_socket:
+ dependency: transitive
+ description:
+ name: shelf_web_socket
+ sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.0"
+ source_gen:
+ dependency: transitive
+ description:
+ name: source_gen
+ sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.0"
+ source_maps:
+ dependency: transitive
+ description:
+ name: source_maps
+ sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.10.13"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.10.2"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.12.1"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ stream_transform:
+ dependency: transitive
+ description:
+ name: stream_transform
+ sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.1"
+ syntax_highlight_lite:
+ dependency: "direct main"
+ description:
+ name: syntax_highlight_lite
+ sha256: ff54f69159263e26b843c64a2dc556ac7bbbc66690eb6d500aefb341cf4957a0
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.0.1"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.2"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.10"
+ timezone:
+ dependency: transitive
+ description:
+ name: timezone
+ sha256: "784a5e34d2eb62e1326f24d6f600aaaee452eb8ca8ef2f384a59244e292d158b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.11.0"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
+ universal_web:
+ dependency: transitive
+ description:
+ name: universal_web
+ sha256: "618a9d8659e9429875f69d4da0fb9c0c143d3d4cdab28b4545502088aa50ed62"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1+1"
+ uuid:
+ dependency: transitive
+ description:
+ name: uuid
+ sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.5.3"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
+ url: "https://pub.dev"
+ source: hosted
+ version: "15.0.2"
+ watcher:
+ dependency: transitive
+ description:
+ name: watcher
+ sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ web_socket:
+ dependency: transitive
+ description:
+ name: web_socket
+ sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
+ web_socket_channel:
+ dependency: transitive
+ description:
+ name: web_socket_channel
+ sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.3"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.3"
+sdks:
+ dart: ">=3.10.0 <3.12.0-z"
diff --git a/site_jaspr/pubspec.yaml b/site_jaspr/pubspec.yaml
new file mode 100644
index 00000000..8c198bcb
--- /dev/null
+++ b/site_jaspr/pubspec.yaml
@@ -0,0 +1,22 @@
+name: site_jaspr
+description: Very Good Workflows documentation site built with Jaspr
+version: 0.0.1
+publish_to: none
+
+environment:
+ sdk: ^3.10.0
+
+dependencies:
+ jaspr: ^0.22.3
+ jaspr_content: ^0.5.0
+ jaspr_router: ^0.8.1
+ syntax_highlight_lite: ^0.0.1
+
+dev_dependencies:
+ build_runner: ^2.10.0
+ build_web_compilers: ^4.4.13
+ jaspr_builder: ^0.22.3
+ lints: ^5.0.0
+
+jaspr:
+ mode: static
diff --git a/site_jaspr/web/.nojekyll b/site_jaspr/web/.nojekyll
new file mode 100644
index 00000000..e69de29b
diff --git a/site_jaspr/web/404.html b/site_jaspr/web/404.html
new file mode 100644
index 00000000..e8475c69
--- /dev/null
+++ b/site_jaspr/web/404.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+ Page Not Found | Very Good Workflows
+
+
+
+
+
+
+
+
+
diff --git a/site_jaspr/web/CNAME b/site_jaspr/web/CNAME
new file mode 100644
index 00000000..596be01b
--- /dev/null
+++ b/site_jaspr/web/CNAME
@@ -0,0 +1 @@
+workflows.vgv.dev
\ No newline at end of file
diff --git a/site_jaspr/web/favicon.ico b/site_jaspr/web/favicon.ico
new file mode 100644
index 00000000..cccc7fcb
Binary files /dev/null and b/site_jaspr/web/favicon.ico differ
diff --git a/site_jaspr/web/images/github.svg b/site_jaspr/web/images/github.svg
new file mode 100644
index 00000000..5044b4d9
--- /dev/null
+++ b/site_jaspr/web/images/github.svg
@@ -0,0 +1 @@
+
diff --git a/site_jaspr/web/images/github_white.svg b/site_jaspr/web/images/github_white.svg
new file mode 100644
index 00000000..0ba5b3c5
--- /dev/null
+++ b/site_jaspr/web/images/github_white.svg
@@ -0,0 +1 @@
+
diff --git a/site_jaspr/web/images/logo.svg b/site_jaspr/web/images/logo.svg
new file mode 100644
index 00000000..f7d29f9e
--- /dev/null
+++ b/site_jaspr/web/images/logo.svg
@@ -0,0 +1,16 @@
+
diff --git a/site_jaspr/web/images/meta/open-graph.png b/site_jaspr/web/images/meta/open-graph.png
new file mode 100644
index 00000000..78dcc9db
Binary files /dev/null and b/site_jaspr/web/images/meta/open-graph.png differ
diff --git a/site_jaspr/web/images/vgv_logo_black.svg b/site_jaspr/web/images/vgv_logo_black.svg
new file mode 100644
index 00000000..c1bf62ce
--- /dev/null
+++ b/site_jaspr/web/images/vgv_logo_black.svg
@@ -0,0 +1,4 @@
+
diff --git a/site_jaspr/web/images/vgv_logo_fill.svg b/site_jaspr/web/images/vgv_logo_fill.svg
new file mode 100644
index 00000000..446c7d7b
--- /dev/null
+++ b/site_jaspr/web/images/vgv_logo_fill.svg
@@ -0,0 +1,14 @@
+
diff --git a/site_jaspr/web/images/workflows_hero.jpeg b/site_jaspr/web/images/workflows_hero.jpeg
new file mode 100644
index 00000000..e7f5d22e
Binary files /dev/null and b/site_jaspr/web/images/workflows_hero.jpeg differ
diff --git a/site_jaspr/web/images/workflows_hero.png b/site_jaspr/web/images/workflows_hero.png
new file mode 100644
index 00000000..a8a089f4
Binary files /dev/null and b/site_jaspr/web/images/workflows_hero.png differ
diff --git a/site_jaspr/web/images/workflows_hero.svg b/site_jaspr/web/images/workflows_hero.svg
new file mode 100644
index 00000000..1cbf4659
--- /dev/null
+++ b/site_jaspr/web/images/workflows_hero.svg
@@ -0,0 +1,179 @@
+
diff --git a/site_jaspr/web/images/workflows_icon.svg b/site_jaspr/web/images/workflows_icon.svg
new file mode 100644
index 00000000..c4358caf
--- /dev/null
+++ b/site_jaspr/web/images/workflows_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/site_jaspr/web/images/workflows_logo.svg b/site_jaspr/web/images/workflows_logo.svg
new file mode 100644
index 00000000..6bcef2db
--- /dev/null
+++ b/site_jaspr/web/images/workflows_logo.svg
@@ -0,0 +1,12 @@
+
diff --git a/site_jaspr/web/images/workflows_logo_dark.svg b/site_jaspr/web/images/workflows_logo_dark.svg
new file mode 100644
index 00000000..c63e6f6b
--- /dev/null
+++ b/site_jaspr/web/images/workflows_logo_dark.svg
@@ -0,0 +1,12 @@
+
diff --git a/site_jaspr/web/images/workflows_nav_icon.svg b/site_jaspr/web/images/workflows_nav_icon.svg
new file mode 100644
index 00000000..a6b161d7
--- /dev/null
+++ b/site_jaspr/web/images/workflows_nav_icon.svg
@@ -0,0 +1,4 @@
+