soames-astro-theme 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +67 -0
  3. package/package.json +68 -0
  4. package/src/components/Bio.tsx +36 -0
  5. package/src/components/BlogSidebar.tsx +48 -0
  6. package/src/components/DocsSidebar.tsx +45 -0
  7. package/src/components/Footer.tsx +59 -0
  8. package/src/components/FooterMenu.tsx +36 -0
  9. package/src/components/Header.tsx +57 -0
  10. package/src/components/HeaderMenu.tsx +77 -0
  11. package/src/components/HeroHeader.tsx +66 -0
  12. package/src/components/Logo.tsx +37 -0
  13. package/src/components/shortcodes/RemoveContentAreaPadding.tsx +13 -0
  14. package/src/components/shortcodes/Shortcodes.tsx +319 -0
  15. package/src/components/shortcodes/SoamesFeature.tsx +54 -0
  16. package/src/components/shortcodes/SoamesGalleryMenu.tsx +90 -0
  17. package/src/components/shortcodes/SoamesIconList.tsx +90 -0
  18. package/src/components/shortcodes/SoamesSoundCloud.tsx +71 -0
  19. package/src/components/shortcodes/SoamesTextBlock.tsx +27 -0
  20. package/src/components/shortcodes/SoamesTextList.tsx +27 -0
  21. package/src/components/shortcodes/SoamesTitle.tsx +24 -0
  22. package/src/components/shortcodes/SoamesTitleBar.tsx +22 -0
  23. package/src/components/shortcodes/SoamesTitleBarLg.tsx +56 -0
  24. package/src/components/shortcodes/SoamesVideo.tsx +35 -0
  25. package/src/components/shortcodes/getAttributes.ts +19 -0
  26. package/src/components/shortcodes/getContent.ts +4 -0
  27. package/src/integration.ts +147 -0
  28. package/src/layouts/Base.astro +93 -0
  29. package/src/lib/wp.ts +351 -0
  30. package/src/routes/[...uri].astro +41 -0
  31. package/src/routes/blog/[...page].astro +88 -0
  32. package/src/routes/blog/post/[...slug].astro +79 -0
  33. package/src/routes/docs/[...slug].astro +58 -0
  34. package/src/routes/docs/index.astro +66 -0
  35. package/src/routes/index.astro +39 -0
  36. package/src/scripts/soames-nav.js +72 -0
  37. package/src/styles/site-overrides.css +4 -0
  38. package/src/styles/soames/base.css +593 -0
  39. package/src/styles/soames/components.css +1556 -0
  40. package/src/styles/soames/layout.css +209 -0
  41. package/src/styles/soames/overrides.css +2138 -0
  42. package/src/styles/soames/typography.css +23 -0
  43. package/src/styles/theme.css +8 -0
  44. package/src/styles/vendor/normalize.css +343 -0
  45. package/src/styles/vendor/wordpress-blocks.css +3451 -0
  46. package/src/theme-shadow.ts +39 -0
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Orbi Software
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ <p align="center">
2
+ <a href="https://soames.app">
3
+ <img alt="Soames" src="https://raw.githubusercontent.com/orbivision/soames-astro-theme/main/assets/soames-mark.svg" width="60" />
4
+ </a>
5
+ </p>
6
+ <h1 align="center">
7
+ Soames Astro Theme
8
+ </h1>
9
+
10
+ [![npm version](https://img.shields.io/npm/v/soames-astro-theme.svg?style=flat-square)](https://www.npmjs.com/package/soames-astro-theme)
11
+ [![license](https://img.shields.io/npm/l/soames-astro-theme.svg?style=flat-square)](./LICENSE)
12
+
13
+ Shared **Astro** theme for the Soames ecosystem — WordPress as a headless CMS,
14
+ static output, React islands. Successor to `soames-gatsby-theme` (see ORBI-23/24/25).
15
+
16
+ ## Install
17
+
18
+ ```
19
+ npm install soames-astro-theme
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```js
25
+ // astro.config.mjs
26
+ import { defineConfig } from 'astro/config';
27
+ import soamesTheme from 'soames-astro-theme';
28
+
29
+ export default defineConfig({
30
+ output: 'static',
31
+ integrations: [
32
+ soamesTheme({ wordpressUrl: process.env.WORDPRESS_GRAPHQL_URL }),
33
+ ],
34
+ });
35
+ ```
36
+
37
+ The integration registers `@astrojs/react`, sources content from WordPress at
38
+ build time, injects the page routes (front page, WP pages, blog archive,
39
+ blog posts), authorizes WP image domains, and installs the **theme-shadow**
40
+ resolver.
41
+
42
+ ## Component overrides (the shadowing successor)
43
+
44
+ Astro has no built-in component shadowing. This theme provides it via a Vite
45
+ resolver: theme files are imported as `@theme/<path>`, and a site overrides any
46
+ of them by creating a file at the matching path under `src/overrides/`.
47
+
48
+ ```
49
+ src/overrides/components/Footer.tsx → replaces the theme's components/Footer.tsx
50
+ src/overrides/layouts/Base.astro → replaces the theme's layouts/Base.astro
51
+ ```
52
+
53
+ Whole-file replacement, resolved at build time, zero changes at the import site —
54
+ the direct successor to Gatsby component shadowing.
55
+
56
+ ## WordPress sourcing notes
57
+
58
+ - Sources via WPGraphQL (`fetch()`), with Site Assets from the Soames plugin REST
59
+ endpoint (`/wp-json/soames/v1/settings`).
60
+ - **Wordfence gotcha:** some WP installs' WAF 403-blocks GraphQL whose first
61
+ selection-set field is `pages`/`posts`. `lib/wp.ts` aliases the root field
62
+ (e.g. `wpPages: pages`) to pass; the durable fix is a Wordfence allowlist rule.
63
+
64
+ ## Status
65
+
66
+ Published to npm (ORBI-25). Under active migration — routing/URL parity, menu
67
+ interactivity, and remaining shortcodes are being completed phase by phase.
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "soames-astro-theme",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "description": "Shared Astro theme for the Soames ecosystem (WordPress headless + React islands). Successor to soames-gatsby-theme.",
6
+ "keywords": [
7
+ "astro",
8
+ "astro-integration",
9
+ "astro-theme",
10
+ "wordpress",
11
+ "headless-cms",
12
+ "typescript",
13
+ "react",
14
+ "blog",
15
+ "personal-site"
16
+ ],
17
+ "author": "Orbi Software",
18
+ "license": "BSD-3-Clause",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/orbivision/soames-astro-theme"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/orbivision/soames-astro-theme/issues"
25
+ },
26
+ "homepage": "https://github.com/orbivision/soames-astro-theme#readme",
27
+ "engines": {
28
+ "node": ">=18.20.8",
29
+ "npm": ">=9.0.0"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "exports": {
35
+ ".": "./src/integration.ts",
36
+ "./integration": "./src/integration.ts",
37
+ "./theme-shadow": "./src/theme-shadow.ts",
38
+ "./lib/wp": "./src/lib/wp.ts",
39
+ "./routes/*": "./src/routes/*",
40
+ "./components/*": "./src/components/*",
41
+ "./layouts/*": "./src/layouts/*",
42
+ "./styles/*": "./src/styles/*",
43
+ "./package.json": "./package.json"
44
+ },
45
+ "files": [
46
+ "src",
47
+ "README.md",
48
+ "LICENSE"
49
+ ],
50
+ "scripts": {
51
+ "check": "astro check"
52
+ },
53
+ "peerDependencies": {
54
+ "astro": "^5.0.0",
55
+ "react": "^18.0.0 || ^19.0.0",
56
+ "react-dom": "^18.0.0 || ^19.0.0"
57
+ },
58
+ "dependencies": {
59
+ "@astrojs/react": "^4.2.0",
60
+ "bootstrap": "^5.3.8",
61
+ "html-react-parser": "^5.2.3"
62
+ },
63
+ "devDependencies": {
64
+ "@types/react": "^18.3.12",
65
+ "@types/react-dom": "^18.3.1",
66
+ "astro": "^5.7.0"
67
+ }
68
+ }
@@ -0,0 +1,36 @@
1
+ // PORTED from soames-gatsby-theme Bio.tsx. Change for Astro: author comes in as a
2
+ // prop (from the post's author node) instead of useStaticQuery(wpUser) — the
3
+ // standalone users query is auth/WAF-gated. Uses firstName, falling back to name.
4
+ import React from "react";
5
+ import type { WpAuthor } from "../lib/wp";
6
+
7
+ interface BioProps {
8
+ author: WpAuthor | null;
9
+ }
10
+
11
+ const Bio: React.FC<BioProps> = ({ author }) => {
12
+ if (!author) return null;
13
+ const displayName = author.firstName || author.name;
14
+ const twitterHandle = author.name;
15
+
16
+ return (
17
+ <div className="bio">
18
+ {author.avatarUrl && (
19
+ <img alt={displayName ?? "Author"} className="bio-avatar" src={author.avatarUrl} />
20
+ )}
21
+ {displayName && (
22
+ <p>
23
+ Written by <strong>{displayName}</strong>
24
+ {author.description ? ` ${author.description}` : ""}{" "}
25
+ {twitterHandle && (
26
+ <a href={`https://twitter.com/${twitterHandle}`}>
27
+ You should follow them on Twitter
28
+ </a>
29
+ )}
30
+ </p>
31
+ )}
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export default Bio;
@@ -0,0 +1,48 @@
1
+ // PORTED from soames-gatsby-theme BlogSidebar.tsx. Changes for Astro: posts come in
2
+ // as a prop (no useStaticQuery); gatsby <Link> → <a>; links use the Astro post path
3
+ // (/blog/post/<slug>/). Markup/classes UNCHANGED.
4
+ import React from "react";
5
+ import parse from "html-react-parser";
6
+ import type { WpPost } from "../lib/wp";
7
+
8
+ interface BlogSidebarProps {
9
+ posts: WpPost[];
10
+ currentId: number;
11
+ }
12
+
13
+ const BlogSidebar: React.FC<BlogSidebarProps> = ({ posts, currentId }) => {
14
+ return (
15
+ <section className="soames-blog-roll pt-3">
16
+ <div className="container">
17
+ {posts.map((post) =>
18
+ post.databaseId !== currentId ? (
19
+ <div key={post.databaseId} className="media-container-row">
20
+ <div className="card p-3 col-12">
21
+ <div className="card-wrapper">
22
+ <div className="card-box">
23
+ <h4 className="card-title mbr-fonts-style display-5">
24
+ {parse(post.title)}
25
+ </h4>
26
+ <h4 className="mbr-fonts-style display-7">{post.date}</h4>
27
+ {parse(post.excerpt)}
28
+ </div>
29
+ <div className="mbr-section-btn text-center">
30
+ <a
31
+ href={`/blog/post/${post.slug}/`}
32
+ itemProp="url"
33
+ className="btn btn-primary display-4"
34
+ >
35
+ <span itemProp="headline">Read More</span>
36
+ </a>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ ) : null
42
+ )}
43
+ </div>
44
+ </section>
45
+ );
46
+ };
47
+
48
+ export default BlogSidebar;
@@ -0,0 +1,45 @@
1
+ // Hierarchical documentation nav for the docs template — the docs equivalent of
2
+ // BlogSidebar's "Recent Posts". Renders the weDocs doc tree (built by
3
+ // buildDocTree) as a nested menu, highlighting the current doc. Static (no
4
+ // hydration needed); links use each doc's WP `uri` (e.g. /docs/getting-started/).
5
+ import React from "react";
6
+ import parse from "html-react-parser";
7
+ import type { DocTreeNode } from "../lib/wp";
8
+
9
+ interface DocsSidebarProps {
10
+ tree: DocTreeNode[];
11
+ currentId: number;
12
+ }
13
+
14
+ const DocList: React.FC<{ nodes: DocTreeNode[]; currentId: number; top?: boolean }> = ({
15
+ nodes,
16
+ currentId,
17
+ top = false,
18
+ }) => (
19
+ <ul className={top ? "soames-docs-menu" : "soames-docs-menu soames-docs-submenu"}>
20
+ {nodes.map((node) => {
21
+ const active = node.databaseId === currentId;
22
+ return (
23
+ <li
24
+ key={node.databaseId}
25
+ className={`soames-docs-menu-item${active ? " is-active" : ""}`}
26
+ >
27
+ <a href={node.uri} aria-current={active ? "page" : undefined}>
28
+ {parse(node.title)}
29
+ </a>
30
+ {node.children.length > 0 && (
31
+ <DocList nodes={node.children} currentId={currentId} />
32
+ )}
33
+ </li>
34
+ );
35
+ })}
36
+ </ul>
37
+ );
38
+
39
+ const DocsSidebar: React.FC<DocsSidebarProps> = ({ tree, currentId }) => (
40
+ <nav className="soames-docs-nav" aria-label="Documentation">
41
+ <DocList nodes={tree} currentId={currentId} top />
42
+ </nav>
43
+ );
44
+
45
+ export default DocsSidebar;
@@ -0,0 +1,59 @@
1
+ // PORTED from soames-gatsby-theme/src/components/Footer.tsx (post-ORBI-22).
2
+ // Changes for Astro: FooterMenu's useStaticQuery → `menuItems` prop (fetched in the
3
+ // layout). Copyright logic (companyName || title) UNCHANGED from ORBI-22.
4
+ import React from "react";
5
+ import FooterMenu from "./FooterMenu";
6
+ import type { MenuItem } from "../lib/wp";
7
+
8
+ interface FooterProps {
9
+ title: string;
10
+ contactBlurb?: string | null;
11
+ companyName?: string | null;
12
+ menuItems?: MenuItem[];
13
+ }
14
+
15
+ const Footer: React.FC<FooterProps> = ({
16
+ title,
17
+ contactBlurb = null,
18
+ companyName = null,
19
+ menuItems = [],
20
+ }) => {
21
+ return (
22
+ <section className="soames-footer mt-5">
23
+ <div className="container">
24
+ <div className="media-container-row content text-white">
25
+ <div className="col-12 col-md-2">
26
+ <div className="media-wrap">{` `}</div>
27
+ </div>
28
+ <div className="col-12 col-md-4 mbr-fonts-style display-7">
29
+ <FooterMenu items={menuItems} />
30
+ </div>
31
+ <div className="col-12 col-md-4 mbr-fonts-style display-7">
32
+ {contactBlurb && (
33
+ <>
34
+ <h5 className="pb-3">Contact</h5>
35
+ <div
36
+ className="soames-text pr-3"
37
+ dangerouslySetInnerHTML={{ __html: contactBlurb }}
38
+ />
39
+ </>
40
+ )}
41
+ <p className="soames-text pt-3" data-testid="footer-copyright">
42
+ © {new Date().getFullYear()} {companyName || title}
43
+ <br />
44
+ Built with{" "}
45
+ <a href="https://www.soames.app" target="_blank" rel="noreferrer">Soames</a>,{" "}
46
+ <a href="https://astro.build" target="_blank" rel="noreferrer">Astro</a>, and{" "}
47
+ <a href="https://wordpress.org/" target="_blank" rel="noreferrer">WordPress</a>
48
+ </p>
49
+ </div>
50
+ <div className="col-12 col-md-2 mbr-fonts-style display-7">
51
+ <h5 className="pb-3">{` `}</h5>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </section>
56
+ );
57
+ };
58
+
59
+ export default Footer;
@@ -0,0 +1,36 @@
1
+ // PORTED from soames-gatsby-theme FooterMenu.tsx. Changes: useStaticQuery → `items`
2
+ // prop (getMenuByLocation("FOOTER")); gatsby <Link> → <a>. Markup UNCHANGED.
3
+ import React from "react";
4
+ import type { MenuItem } from "../lib/wp";
5
+
6
+ interface FooterMenuProps {
7
+ items: MenuItem[];
8
+ }
9
+
10
+ const FooterMenu: React.FC<FooterMenuProps> = ({ items }) => {
11
+ const topLevel = items.filter((item) => item.parentDatabaseId === 0);
12
+ if (topLevel.length === 0) return null;
13
+
14
+ return (
15
+ <div className="soames-footer-content">
16
+ <h5 className="pb-3">Links</h5>
17
+ <ul>
18
+ {topLevel.map((item) =>
19
+ item.uri.includes("http") ? (
20
+ <li className="soames-footer-list-item" key={item.id}>
21
+ <a href={item.uri} target="_blank" rel="noreferrer">
22
+ {item.label}
23
+ </a>
24
+ </li>
25
+ ) : (
26
+ <li className="soames-footer-list-item" key={item.id}>
27
+ <a href={item.uri}>{item.label}</a>
28
+ </li>
29
+ )
30
+ )}
31
+ </ul>
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export default FooterMenu;
@@ -0,0 +1,57 @@
1
+ // PORTED from soames-gatsby-theme Header.tsx. Logo/menu data now flow as props
2
+ // (no useStaticQuery). Markup UNCHANGED.
3
+ import React from "react";
4
+ import HeaderMenu from "./HeaderMenu";
5
+ import Logo from "./Logo";
6
+ import type { MenuItem } from "../lib/wp";
7
+
8
+ interface HeaderProps {
9
+ title: string;
10
+ logoUrl?: string | null;
11
+ logoAlt?: string | null;
12
+ companyName?: string | null;
13
+ showCompanyName?: boolean;
14
+ menuItems: MenuItem[];
15
+ }
16
+
17
+ const Header: React.FC<HeaderProps> = ({
18
+ title,
19
+ logoUrl = null,
20
+ logoAlt = null,
21
+ companyName = null,
22
+ showCompanyName = true,
23
+ menuItems,
24
+ }) => {
25
+ return (
26
+ <section className="menu soames-menu">
27
+ <nav className="navbar navbar-expand beta-menu navbar-dropdown align-items-center navbar-fixed-top navbar-toggleable-sm">
28
+ <button
29
+ className="navbar-toggler navbar-toggler-right"
30
+ type="button"
31
+ data-bs-toggle="collapse"
32
+ data-bs-target="#navbarSupportedContent"
33
+ aria-controls="navbarSupportedContent"
34
+ aria-expanded="false"
35
+ aria-label="Toggle navigation"
36
+ >
37
+ <div className="hamburger">
38
+ <span></span>
39
+ <span></span>
40
+ <span></span>
41
+ <span></span>
42
+ </div>
43
+ </button>
44
+ <Logo
45
+ title={title}
46
+ logoUrl={logoUrl}
47
+ logoAlt={logoAlt}
48
+ companyName={companyName}
49
+ showCompanyName={showCompanyName}
50
+ />
51
+ <HeaderMenu items={menuItems} />
52
+ </nav>
53
+ </section>
54
+ );
55
+ };
56
+
57
+ export default Header;
@@ -0,0 +1,77 @@
1
+ // PORTED from soames-gatsby-theme HeaderMenu.tsx. Changes: useStaticQuery → `items`
2
+ // prop (fetched in the layout via getMenuByLocation("HEADER")); gatsby <Link> → <a>.
3
+ // Markup/classes UNCHANGED — Bootstrap dropdown behavior is wired by global JS
4
+ // (Phase 4), not React, so this renders as static HTML.
5
+ import React from "react";
6
+ import type { MenuItem } from "../lib/wp";
7
+
8
+ interface HeaderMenuProps {
9
+ items: MenuItem[];
10
+ }
11
+
12
+ const HeaderMenu: React.FC<HeaderMenuProps> = ({ items }) => {
13
+ return (
14
+ <div className="collapse navbar-collapse" id="navbarSupportedContent">
15
+ <ul className="navbar-nav nav-dropdown nav-right" data-app-modern-menu="true">
16
+ {items.map((item) =>
17
+ item.path !== "/home/" && item.parentDatabaseId === 0 ? (
18
+ item.childItems.length === 0 ? (
19
+ <li key={item.id} className="nav-item">
20
+ {item.uri.includes("http") ? (
21
+ <a
22
+ className="nav-link link text-white display-4"
23
+ href={item.uri}
24
+ target="_blank"
25
+ rel="noreferrer"
26
+ >
27
+ {item.label}
28
+ </a>
29
+ ) : (
30
+ <a href={item.uri} className="nav-link link text-white display-4">
31
+ {item.label}
32
+ </a>
33
+ )}
34
+ </li>
35
+ ) : (
36
+ <li key={item.id} className="nav-item dropdown">
37
+ <a
38
+ className="nav-link link text-white dropdown-toggle display-4"
39
+ href={item.uri}
40
+ data-toggle="dropdown-submenu"
41
+ aria-expanded="false"
42
+ >
43
+ {item.label}
44
+ </a>
45
+ <div className="dropdown-menu">
46
+ <ul className="navbar-nav nav-dropdown nav-right">
47
+ {item.childItems.map((childItem) => (
48
+ <li key={childItem.id}>
49
+ {childItem.uri.includes("http") ? (
50
+ <a
51
+ className="text-white dropdown-item display-4"
52
+ target="_blank"
53
+ rel="noreferrer"
54
+ href={childItem.uri}
55
+ >
56
+ {childItem.label}
57
+ <br />
58
+ </a>
59
+ ) : (
60
+ <a href={childItem.uri} className="text-white dropdown-item display-4">
61
+ {childItem.label}
62
+ </a>
63
+ )}
64
+ </li>
65
+ ))}
66
+ </ul>
67
+ </div>
68
+ </li>
69
+ )
70
+ ) : null
71
+ )}
72
+ </ul>
73
+ </div>
74
+ );
75
+ };
76
+
77
+ export default HeaderMenu;
@@ -0,0 +1,66 @@
1
+ // PORTED from soames-gatsby-theme HeroHeader.tsx. Change for Astro: title/subhead
2
+ // come in as HTML strings and are run through html-react-parser here (the Gatsby
3
+ // page template parsed them before passing). Parallax/overlay markup UNCHANGED.
4
+ import React from "react";
5
+ import parse from "html-react-parser";
6
+
7
+ export interface HeroHeaderProps {
8
+ title: string;
9
+ subhead?: string | null;
10
+ backgroundImage?: string | null;
11
+ overlayOpacity?: number | null;
12
+ }
13
+
14
+ const HeroHeader: React.FC<HeroHeaderProps> = ({
15
+ title,
16
+ subhead,
17
+ backgroundImage,
18
+ overlayOpacity,
19
+ }) => {
20
+ const bg = backgroundImage || "https://picsum.photos/1080/720";
21
+ const opacity = overlayOpacity ?? 0.6;
22
+
23
+ const css = `
24
+ .soames-background-lg::after {
25
+ background: url(${bg});
26
+ background-position: 50% 50%;
27
+ background-size: cover;
28
+ background-repeat: no-repeat;
29
+ position: fixed;
30
+ top: 0px;
31
+ left: 0px;
32
+ overflow: hidden;
33
+ pointer-events: none;
34
+ margin-top: -180px;
35
+ }
36
+ `;
37
+
38
+ return (
39
+ <>
40
+ <style>{css}</style>
41
+ <section
42
+ className="soames-header-lg soames-parallax soames-background-lg"
43
+ id="header1"
44
+ >
45
+ <div
46
+ className="soames-overlay"
47
+ style={{ opacity, backgroundColor: "rgb(46, 46, 46)" }}
48
+ />
49
+ <div className="container">
50
+ <div className="row justify-content-md-center">
51
+ <div className="soames-hero-header soames-white col-md-10">
52
+ <h1 className="soames-section-title align-center soames-bold mbr-fonts-style display-1">
53
+ {parse(title || "")}
54
+ </h1>
55
+ <div className="soames-section-subtitle align-center soames-light soames-white mbr-fonts-style display-5">
56
+ {subhead ? parse(subhead) : ""}
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </section>
62
+ </>
63
+ );
64
+ };
65
+
66
+ export default HeroHeader;
@@ -0,0 +1,37 @@
1
+ // PORTED from soames-gatsby-theme Logo.tsx. Change: useStaticQuery(soamesSettings)
2
+ // → props supplied by the layout at build time (Astro has no useStaticQuery).
3
+ import React from "react";
4
+
5
+ interface LogoProps {
6
+ title: string;
7
+ logoUrl?: string | null;
8
+ logoAlt?: string | null;
9
+ companyName?: string | null;
10
+ showCompanyName?: boolean;
11
+ }
12
+
13
+ const Logo: React.FC<LogoProps> = ({
14
+ title,
15
+ logoUrl = null,
16
+ logoAlt = null,
17
+ companyName = null,
18
+ showCompanyName = true,
19
+ }) => {
20
+ const displayName = companyName || title;
21
+ const alt = logoAlt || title;
22
+
23
+ return (
24
+ <div className="menu-logo">
25
+ <div className="navbar-brand">
26
+ <span className="navbar-caption-wrap">
27
+ <a className="navbar-caption text-white display-5" href="/">
28
+ {logoUrl && <img width="108" alt={alt} src={logoUrl} />}
29
+ {showCompanyName && <>&nbsp;&nbsp;{displayName}</>}
30
+ </a>
31
+ </span>
32
+ </div>
33
+ </div>
34
+ );
35
+ };
36
+
37
+ export default Logo;
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+
3
+ const RemoveContentAreaPadding: React.FC = () => {
4
+ const css = `
5
+ .soames-gatsby-content {
6
+ padding: 0px;
7
+ }
8
+ `;
9
+
10
+ return <style>{css}</style>;
11
+ };
12
+
13
+ export default RemoveContentAreaPadding;