@grantcodes/ui 2.0.2 → 2.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.
- package/CHANGELOG.md +7 -0
- package/custom-elements.json +1926 -191
- package/package.json +6 -5
- package/src/components/accordion/accordion.component.js +33 -0
- package/src/components/accordion/accordion.js +6 -0
- package/src/components/accordion/accordion.stories.js +88 -0
- package/src/components/accordion/accordion.styles.js +66 -0
- package/src/components/accordion/index.js +6 -0
- package/src/components/app-bar/app-bar.component.js +1 -3
- package/src/components/app-bar/app-bar.js +0 -2
- package/src/components/app-bar/app-bar.styles.js +222 -221
- package/src/components/app-bar/app-bar.test.js +58 -17
- package/src/components/app-bar/index.js +0 -2
- package/src/components/avatar/avatar.js +0 -12
- package/src/components/avatar/avatar.stories.js +0 -12
- package/src/components/avatar/avatar.styles.js +19 -19
- package/src/components/avatar/avatar.test.js +4 -4
- package/src/components/avatar/index.js +1 -13
- package/src/components/badge/badge.js +0 -2
- package/src/components/badge/badge.styles.js +78 -81
- package/src/components/badge/badge.test.js +18 -5
- package/src/components/badge/index.js +0 -2
- package/src/components/breadcrumb/breadcrumb.component.js +9 -10
- package/src/components/breadcrumb/breadcrumb.js +6 -4
- package/src/components/breadcrumb/breadcrumb.styles.js +86 -90
- package/src/components/breadcrumb/breadcrumb.test.js +15 -5
- package/src/components/breadcrumb/index.js +0 -2
- package/src/components/button/button.component.js +2 -2
- package/src/components/button/button.styles.js +58 -86
- package/src/components/button/button.test.js +8 -4
- package/src/components/button/index.js +1 -1
- package/src/components/button-group/button-group.test.js +0 -2
- package/src/components/button-group/index.js +1 -1
- package/src/components/card/card.component.js +40 -9
- package/src/components/card/card.js +3 -1
- package/src/components/card/card.stories.js +18 -5
- package/src/components/card/card.styles.js +46 -20
- package/src/components/card/card.test.js +0 -2
- package/src/components/card/index.js +1 -1
- package/src/components/code-preview/code-preview.component.js +9 -9
- package/src/components/code-preview/code-preview.js +0 -1
- package/src/components/code-preview/code-preview.styles.js +3 -3
- package/src/components/code-preview/code-preview.test.js +29 -8
- package/src/components/code-preview/index.js +1 -1
- package/src/components/container/container.component.js +1 -0
- package/src/components/container/container.js +0 -1
- package/src/components/container/container.stories.js +12 -4
- package/src/components/container/container.styles.js +37 -35
- package/src/components/container/container.test.js +0 -2
- package/src/components/container/index.js +1 -1
- package/src/components/cta/cta.component.js +108 -0
- package/src/components/cta/cta.js +6 -0
- package/src/components/cta/cta.stories.js +56 -0
- package/src/components/cta/cta.styles.js +64 -0
- package/src/components/cta/index.js +1 -0
- package/src/components/dialog/dialog.js +0 -1
- package/src/components/dialog/dialog.styles.js +8 -8
- package/src/components/dialog/dialog.test.js +11 -5
- package/src/components/dialog/index.js +1 -1
- package/src/components/dropdown/dropdown.component.js +5 -3
- package/src/components/dropdown/dropdown.js +6 -4
- package/src/components/dropdown/dropdown.styles.js +5 -5
- package/src/components/dropdown/dropdown.test.js +20 -4
- package/src/components/dropdown/index.js +0 -2
- package/src/components/dropzone/dropzone.component.js +7 -6
- package/src/components/dropzone/dropzone.styles.js +4 -4
- package/src/components/dropzone/dropzone.test.js +6 -4
- package/src/components/dropzone/index.js +1 -1
- package/src/components/feature-list/feature-list.component.js +130 -0
- package/src/components/feature-list/feature-list.js +6 -0
- package/src/components/feature-list/feature-list.stories.js +117 -0
- package/src/components/feature-list/feature-list.styles.js +82 -0
- package/src/components/feature-list/index.js +1 -0
- package/src/components/footer/footer-column.styles.js +46 -47
- package/src/components/footer/footer.js +6 -2
- package/src/components/footer/footer.styles.js +6 -6
- package/src/components/footer/footer.test.js +9 -4
- package/src/components/footer/index.js +1 -1
- package/src/components/form-field/form-field.component.js +1 -3
- package/src/components/form-field/form-field.js +0 -1
- package/src/components/form-field/form-field.styles.js +35 -37
- package/src/components/form-field/form-field.test.js +9 -4
- package/src/components/form-field/index.js +1 -1
- package/src/components/gallery/gallery-image.js +0 -1
- package/src/components/gallery/gallery.js +0 -1
- package/src/components/gallery/gallery.styles.js +1 -1
- package/src/components/gallery/gallery.test.js +5 -3
- package/src/components/gallery/index.js +2 -2
- package/src/components/hero/hero.component.js +66 -0
- package/src/components/hero/hero.js +6 -0
- package/src/components/hero/hero.stories.js +53 -0
- package/src/components/hero/hero.styles.js +46 -0
- package/src/components/hero/index.js +1 -0
- package/src/components/icon/icon.js +3 -2
- package/src/components/icon/icon.stories.js +2 -1
- package/src/components/icon/icon.styles.js +23 -21
- package/src/components/icon/icon.test.js +2 -3
- package/src/components/icon/index.js +1 -1
- package/src/components/loading/index.js +1 -1
- package/src/components/loading/loading.js +3 -2
- package/src/components/loading/loading.styles.js +1 -1
- package/src/components/loading/loading.test.js +0 -2
- package/src/components/logo-cloud/index.js +1 -0
- package/src/components/logo-cloud/logo-cloud.component.js +81 -0
- package/src/components/logo-cloud/logo-cloud.js +6 -0
- package/src/components/logo-cloud/logo-cloud.stories.js +107 -0
- package/src/components/logo-cloud/logo-cloud.styles.js +68 -0
- package/src/components/media-text/index.js +1 -0
- package/src/components/media-text/media-text.component.js +100 -0
- package/src/components/media-text/media-text.js +6 -0
- package/src/components/media-text/media-text.stories.js +69 -0
- package/src/components/media-text/media-text.styles.js +66 -0
- package/src/components/newsletter/index.js +1 -0
- package/src/components/newsletter/newsletter.component.js +101 -0
- package/src/components/newsletter/newsletter.js +6 -0
- package/src/components/newsletter/newsletter.stories.js +59 -0
- package/src/components/newsletter/newsletter.styles.js +89 -0
- package/src/components/notice/index.js +1 -1
- package/src/components/notice/notice.js +0 -1
- package/src/components/notice/notice.styles.js +7 -7
- package/src/components/notice/notice.test.js +15 -5
- package/src/components/pagination/index.js +1 -1
- package/src/components/pagination/pagination.stories.js +1 -3
- package/src/components/pagination/pagination.styles.js +1 -1
- package/src/components/pagination/pagination.test.js +9 -4
- package/src/components/pricing/index.js +1 -0
- package/src/components/pricing/pricing.component.js +119 -0
- package/src/components/pricing/pricing.js +6 -0
- package/src/components/pricing/pricing.stories.js +123 -0
- package/src/components/pricing/pricing.styles.js +135 -0
- package/src/components/sidebar/index.js +0 -2
- package/src/components/sidebar/sidebar.component.js +12 -10
- package/src/components/sidebar/sidebar.js +3 -3
- package/src/components/sidebar/sidebar.stories.js +0 -2
- package/src/components/sidebar/sidebar.styles.js +181 -186
- package/src/components/sidebar/sidebar.test.js +48 -13
- package/src/components/stats/index.js +1 -0
- package/src/components/stats/stats.component.js +73 -0
- package/src/components/stats/stats.js +6 -0
- package/src/components/stats/stats.stories.js +64 -0
- package/src/components/stats/stats.styles.js +66 -0
- package/src/components/tabs/index.js +2 -2
- package/src/components/tabs/internal/tabs-button.component.js +1 -1
- package/src/components/tabs/internal/tabs-button.js +0 -1
- package/src/components/tabs/tab.js +0 -1
- package/src/components/tabs/tabs.js +3 -2
- package/src/components/tabs/tabs.styles.js +84 -74
- package/src/components/testimonials/index.js +1 -0
- package/src/components/testimonials/testimonials.component.js +97 -0
- package/src/components/testimonials/testimonials.js +6 -0
- package/src/components/testimonials/testimonials.stories.js +78 -0
- package/src/components/testimonials/testimonials.styles.js +82 -0
- package/src/components/toast/index.js +0 -2
- package/src/components/toast/toast.component.js +1 -3
- package/src/components/toast/toast.js +10 -5
- package/src/components/toast/toast.stories.js +9 -5
- package/src/components/toast/toast.styles.js +199 -201
- package/src/components/toast/toast.test.js +38 -10
- package/src/components/tooltip/index.js +1 -1
- package/src/components/tooltip/tooltip.js +3 -2
- package/src/components/tooltip/tooltip.styles.js +3 -3
- package/src/components/tooltip/tooltip.test.js +10 -4
- package/src/css/base.css +8 -5
- package/src/css/colors.stories.js +27 -28
- package/src/css/elements/forms/input.css +9 -41
- package/src/css/elements/media/image.css +1 -1
- package/src/css/themes/todomap.css +1 -0
- package/src/css/tokens.stories.js +26 -21
- package/src/css/typography.css +1 -3
- package/src/css/util/focus-ring.css +30 -0
- package/src/css/util/index.css +1 -2
- package/src/lib/styles/focus-ring.styles.js +34 -0
- package/src/main.js +10 -1
- package/src/pages/agency.stories.js +164 -0
- package/src/pages/blog-post.stories.js +381 -0
- package/src/pages/saas-landing.stories.js +307 -0
- package/src/test-utils/assert-helpers.js +10 -8
- package/src/css/util/functions.css +0 -16
- package/src/css/util/mixins.css +0 -63
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import "../components/app-bar/app-bar.js";
|
|
3
|
+
import "../components/button/button.js";
|
|
4
|
+
import "../components/breadcrumb/breadcrumb.js";
|
|
5
|
+
import "../components/container/container.js";
|
|
6
|
+
import "../components/badge/badge.js";
|
|
7
|
+
import "../components/avatar/avatar.js";
|
|
8
|
+
import "../components/card/card.js";
|
|
9
|
+
import "../components/cta/cta.js";
|
|
10
|
+
import "../components/footer/footer.js";
|
|
11
|
+
|
|
12
|
+
const meta = {
|
|
13
|
+
title: "Pages/Blog Post",
|
|
14
|
+
parameters: {
|
|
15
|
+
layout: "fullscreen",
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
|
|
21
|
+
const relatedPosts = [
|
|
22
|
+
{
|
|
23
|
+
category: "Engineering",
|
|
24
|
+
title: "How we cut our build times by 60% with better caching",
|
|
25
|
+
excerpt:
|
|
26
|
+
"A deep dive into the caching strategies we adopted across our CI pipeline and how they compound over time.",
|
|
27
|
+
author: { name: "Priya Nair", avatar: "https://i.pravatar.cc/80?img=32" },
|
|
28
|
+
date: "Feb 28, 2025",
|
|
29
|
+
href: "/blog/build-cache",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
category: "Design",
|
|
33
|
+
title: "Writing design tokens that scale across themes",
|
|
34
|
+
excerpt:
|
|
35
|
+
"Tokens are only as good as the naming conventions behind them. Here's what we learned from three rewrites.",
|
|
36
|
+
author: { name: "Leo Hartmann", avatar: "https://i.pravatar.cc/80?img=53" },
|
|
37
|
+
date: "Feb 14, 2025",
|
|
38
|
+
href: "/blog/design-tokens",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
category: "Culture",
|
|
42
|
+
title: "Small teams ship better software — here's why",
|
|
43
|
+
excerpt:
|
|
44
|
+
"Coordination cost grows faster than headcount. We explore the research and what it means for how we hire.",
|
|
45
|
+
author: {
|
|
46
|
+
name: "Chloe Bergstrom",
|
|
47
|
+
avatar: "https://i.pravatar.cc/80?img=25",
|
|
48
|
+
},
|
|
49
|
+
date: "Jan 30, 2025",
|
|
50
|
+
href: "/blog/small-teams",
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const footerContent = html`
|
|
55
|
+
<grantcodes-footer-column>
|
|
56
|
+
<h3>Flowbase</h3>
|
|
57
|
+
<p>The modern platform for teams that move fast and ship great software.</p>
|
|
58
|
+
</grantcodes-footer-column>
|
|
59
|
+
|
|
60
|
+
<grantcodes-footer-column>
|
|
61
|
+
<h3>Product</h3>
|
|
62
|
+
<ul>
|
|
63
|
+
<li><a href="/features">Features</a></li>
|
|
64
|
+
<li><a href="/pricing">Pricing</a></li>
|
|
65
|
+
<li><a href="/changelog">Changelog</a></li>
|
|
66
|
+
</ul>
|
|
67
|
+
</grantcodes-footer-column>
|
|
68
|
+
|
|
69
|
+
<grantcodes-footer-column>
|
|
70
|
+
<h3>Company</h3>
|
|
71
|
+
<ul>
|
|
72
|
+
<li><a href="/blog">Blog</a></li>
|
|
73
|
+
<li><a href="/about">About</a></li>
|
|
74
|
+
<li><a href="/careers">Careers</a></li>
|
|
75
|
+
</ul>
|
|
76
|
+
</grantcodes-footer-column>
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A full blog post page. Demonstrates a reading-focused layout using the
|
|
81
|
+
* container component for column width control, breadcrumb for navigation
|
|
82
|
+
* context, badge for category tagging, avatar for the author byline, and
|
|
83
|
+
* cards for related posts at the bottom.
|
|
84
|
+
*/
|
|
85
|
+
export const Default = {
|
|
86
|
+
render: () => html`
|
|
87
|
+
<grantcodes-app-bar sticky>
|
|
88
|
+
<a
|
|
89
|
+
slot="logo"
|
|
90
|
+
href="/"
|
|
91
|
+
style="font-weight: 700; font-size: 1.25rem; text-decoration: none; color: inherit;"
|
|
92
|
+
>
|
|
93
|
+
Flowbase
|
|
94
|
+
</a>
|
|
95
|
+
<div slot="nav" style="display: flex; gap: 0.5rem;">
|
|
96
|
+
<a href="/">Home</a>
|
|
97
|
+
<a href="/features">Features</a>
|
|
98
|
+
<a href="/blog">Blog</a>
|
|
99
|
+
<a href="/pricing">Pricing</a>
|
|
100
|
+
</div>
|
|
101
|
+
<div slot="actions" style="display: flex; gap: 0.5rem;">
|
|
102
|
+
<grantcodes-button variant="ghost">Sign in</grantcodes-button>
|
|
103
|
+
<grantcodes-button>Get started</grantcodes-button>
|
|
104
|
+
</div>
|
|
105
|
+
</grantcodes-app-bar>
|
|
106
|
+
|
|
107
|
+
<!-- Article header -->
|
|
108
|
+
<grantcodes-container
|
|
109
|
+
style="padding-block-start: var(--g-theme-spacing-xl);"
|
|
110
|
+
>
|
|
111
|
+
<div style="max-inline-size: 48rem; margin-inline: auto;">
|
|
112
|
+
<grantcodes-breadcrumb>
|
|
113
|
+
<grantcodes-breadcrumb-item href="/">Home</grantcodes-breadcrumb-item>
|
|
114
|
+
<grantcodes-breadcrumb-item href="/blog"
|
|
115
|
+
>Blog</grantcodes-breadcrumb-item
|
|
116
|
+
>
|
|
117
|
+
<grantcodes-breadcrumb-item current
|
|
118
|
+
>Shipping faster with async-first teams</grantcodes-breadcrumb-item
|
|
119
|
+
>
|
|
120
|
+
</grantcodes-breadcrumb>
|
|
121
|
+
|
|
122
|
+
<div
|
|
123
|
+
style="margin-block-start: var(--g-theme-spacing-lg); display: flex; flex-direction: column; gap: var(--g-theme-spacing-md);"
|
|
124
|
+
>
|
|
125
|
+
<grantcodes-badge variant="primary">Engineering</grantcodes-badge>
|
|
126
|
+
|
|
127
|
+
<h1
|
|
128
|
+
style="margin: 0; font-size: var(--g-theme-typography-display-default-font-size); font-weight: var(--g-theme-typography-display-default-font-weight); line-height: 1.15; color: var(--g-theme-color-content-default);"
|
|
129
|
+
>
|
|
130
|
+
Shipping faster with async-first teams
|
|
131
|
+
</h1>
|
|
132
|
+
|
|
133
|
+
<p
|
|
134
|
+
style="margin: 0; font-size: var(--g-theme-typography-body-lg-font-size); color: var(--g-theme-color-content-secondary); line-height: 1.6;"
|
|
135
|
+
>
|
|
136
|
+
How we restructured communication to remove the hidden cost of
|
|
137
|
+
synchronous coordination — and what we shipped as a result.
|
|
138
|
+
</p>
|
|
139
|
+
|
|
140
|
+
<div
|
|
141
|
+
style="display: flex; align-items: center; gap: var(--g-theme-spacing-sm); padding-block: var(--g-theme-spacing-md); border-block: 1px solid var(--g-theme-color-border-default);"
|
|
142
|
+
>
|
|
143
|
+
<grantcodes-avatar
|
|
144
|
+
src="https://i.pravatar.cc/80?img=12"
|
|
145
|
+
name="Sam Torres"
|
|
146
|
+
size="small"
|
|
147
|
+
></grantcodes-avatar>
|
|
148
|
+
<div>
|
|
149
|
+
<p
|
|
150
|
+
style="margin: 0; font-weight: var(--g-typography-font-weight-600); color: var(--g-theme-color-content-default); font-size: var(--g-theme-typography-label-default-font-size);"
|
|
151
|
+
>
|
|
152
|
+
Sam Torres
|
|
153
|
+
</p>
|
|
154
|
+
<p
|
|
155
|
+
style="margin: 0; color: var(--g-theme-color-content-secondary); font-size: var(--g-theme-typography-label-sm-font-size);"
|
|
156
|
+
>
|
|
157
|
+
March 12, 2025 · 8 min read
|
|
158
|
+
</p>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</grantcodes-container>
|
|
164
|
+
|
|
165
|
+
<!-- Featured image -->
|
|
166
|
+
<grantcodes-container
|
|
167
|
+
nopad
|
|
168
|
+
style="margin-block: var(--g-theme-spacing-lg);"
|
|
169
|
+
>
|
|
170
|
+
<img
|
|
171
|
+
src="https://placehold.co/1200x500?text=Featured+Image"
|
|
172
|
+
alt="An abstract illustration of async communication flows"
|
|
173
|
+
style="display: block; width: 100%; height: auto; border-radius: var(--g-theme-border-radius-md);"
|
|
174
|
+
/>
|
|
175
|
+
</grantcodes-container>
|
|
176
|
+
|
|
177
|
+
<!-- Article body -->
|
|
178
|
+
<grantcodes-container
|
|
179
|
+
align="prose"
|
|
180
|
+
style="padding-block-end: var(--g-theme-spacing-2xl);"
|
|
181
|
+
>
|
|
182
|
+
<article>
|
|
183
|
+
<p>
|
|
184
|
+
Every team I've worked on has had the same default: when in doubt,
|
|
185
|
+
schedule a meeting. It feels productive. You get everyone in the room,
|
|
186
|
+
align on something, and leave with decisions. But the cost is
|
|
187
|
+
invisible — it shows up later, in the form of engineers who can't
|
|
188
|
+
reach a flow state, PMs who spend half their week in Zoom, and
|
|
189
|
+
decisions that only one person truly understands.
|
|
190
|
+
</p>
|
|
191
|
+
|
|
192
|
+
<p>
|
|
193
|
+
When we went async-first eighteen months ago, I expected some
|
|
194
|
+
friction. What I didn't expect was how much <em>clarity</em> it would
|
|
195
|
+
force.
|
|
196
|
+
</p>
|
|
197
|
+
|
|
198
|
+
<h2>What async-first actually means</h2>
|
|
199
|
+
|
|
200
|
+
<p>
|
|
201
|
+
Async-first doesn't mean no meetings. It means every meeting has to
|
|
202
|
+
earn its place. Before we schedule time, we ask: can this be a
|
|
203
|
+
document? Can this be a comment thread? Can this decision be made by
|
|
204
|
+
one person and communicated to the rest?
|
|
205
|
+
</p>
|
|
206
|
+
|
|
207
|
+
<p>
|
|
208
|
+
The answer is yes more often than you'd think. Most "alignment
|
|
209
|
+
meetings" are actually announcements. Most "brainstorms" are better
|
|
210
|
+
done in writing first, then refined synchronously if needed.
|
|
211
|
+
</p>
|
|
212
|
+
|
|
213
|
+
<blockquote>
|
|
214
|
+
<p>
|
|
215
|
+
Writing forces clarity. If you can't explain your proposal in a
|
|
216
|
+
document, you don't understand it well enough to decide on it yet.
|
|
217
|
+
</p>
|
|
218
|
+
</blockquote>
|
|
219
|
+
|
|
220
|
+
<h2>The three tools we leaned on</h2>
|
|
221
|
+
|
|
222
|
+
<p>
|
|
223
|
+
We standardised on three artefacts that replaced most of our recurring
|
|
224
|
+
meetings:
|
|
225
|
+
</p>
|
|
226
|
+
|
|
227
|
+
<ol>
|
|
228
|
+
<li>
|
|
229
|
+
<strong>Weekly written updates.</strong> Each team member posts a
|
|
230
|
+
short update every Monday: what shipped, what's in progress, what's
|
|
231
|
+
blocked. No meeting required.
|
|
232
|
+
</li>
|
|
233
|
+
<li>
|
|
234
|
+
<strong>Decision documents.</strong> Any significant technical or
|
|
235
|
+
product decision gets a one-pager. Context, options, recommendation,
|
|
236
|
+
decision. It lives in the repo, not in someone's memory.
|
|
237
|
+
</li>
|
|
238
|
+
<li>
|
|
239
|
+
<strong>Structured async reviews.</strong> Design and code reviews
|
|
240
|
+
happen in writing first. We only meet if the written discussion
|
|
241
|
+
reaches an impasse after 48 hours.
|
|
242
|
+
</li>
|
|
243
|
+
</ol>
|
|
244
|
+
|
|
245
|
+
<h2>What we shipped as a result</h2>
|
|
246
|
+
|
|
247
|
+
<p>
|
|
248
|
+
In the six months after the shift, our cycle time dropped by 40%. Not
|
|
249
|
+
because we worked more hours — we actually worked fewer. The
|
|
250
|
+
difference was that engineers had longer uninterrupted stretches,
|
|
251
|
+
decisions were made and recorded clearly, and we stopped relitigating
|
|
252
|
+
things we'd already decided in a meeting no one documented.
|
|
253
|
+
</p>
|
|
254
|
+
|
|
255
|
+
<p>
|
|
256
|
+
The compounding effect of async-first is slow but real. Each decision
|
|
257
|
+
document becomes a reference for the next one. Each written update
|
|
258
|
+
builds a log that onboards new hires in days rather than weeks. The
|
|
259
|
+
knowledge stops living in people's heads and starts living in the
|
|
260
|
+
system.
|
|
261
|
+
</p>
|
|
262
|
+
|
|
263
|
+
<div
|
|
264
|
+
style="margin-block-start: var(--g-theme-spacing-xl); padding: var(--g-theme-spacing-md); background: var(--g-theme-color-background-subtle); border-radius: var(--g-theme-border-radius-md); display: flex; align-items: center; gap: var(--g-theme-spacing-md);"
|
|
265
|
+
>
|
|
266
|
+
<grantcodes-avatar
|
|
267
|
+
src="https://i.pravatar.cc/80?img=12"
|
|
268
|
+
name="Sam Torres"
|
|
269
|
+
size="medium"
|
|
270
|
+
></grantcodes-avatar>
|
|
271
|
+
<div>
|
|
272
|
+
<p
|
|
273
|
+
style="margin: 0; font-weight: var(--g-typography-font-weight-600); color: var(--g-theme-color-content-default);"
|
|
274
|
+
>
|
|
275
|
+
Sam Torres
|
|
276
|
+
</p>
|
|
277
|
+
<p
|
|
278
|
+
style="margin: 0; color: var(--g-theme-color-content-secondary); font-size: var(--g-theme-typography-body-sm-font-size);"
|
|
279
|
+
>
|
|
280
|
+
CTO at Flowbase. Writes about engineering culture, distributed
|
|
281
|
+
teams, and systems thinking.
|
|
282
|
+
</p>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
</article>
|
|
286
|
+
</grantcodes-container>
|
|
287
|
+
|
|
288
|
+
<!-- Related posts -->
|
|
289
|
+
<div
|
|
290
|
+
style="background: var(--g-theme-color-background-subtle); padding-block: var(--g-theme-spacing-2xl);"
|
|
291
|
+
>
|
|
292
|
+
<grantcodes-container>
|
|
293
|
+
<h2
|
|
294
|
+
style="margin: 0 0 var(--g-theme-spacing-lg); font-size: var(--g-theme-typography-headline-default-font-size); font-weight: var(--g-theme-typography-headline-default-font-weight); color: var(--g-theme-color-content-default);"
|
|
295
|
+
>
|
|
296
|
+
More from the blog
|
|
297
|
+
</h2>
|
|
298
|
+
<div
|
|
299
|
+
style="display: grid; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); gap: var(--g-theme-spacing-lg);"
|
|
300
|
+
>
|
|
301
|
+
${relatedPosts.map(
|
|
302
|
+
(post) => html`
|
|
303
|
+
<grantcodes-card>
|
|
304
|
+
<div
|
|
305
|
+
slot="header"
|
|
306
|
+
style="display: flex; align-items: center; justify-content: space-between;"
|
|
307
|
+
>
|
|
308
|
+
<grantcodes-badge variant="neutral"
|
|
309
|
+
>${post.category}</grantcodes-badge
|
|
310
|
+
>
|
|
311
|
+
<span
|
|
312
|
+
style="font-size: var(--g-theme-typography-label-sm-font-size); color: var(--g-theme-color-content-secondary);"
|
|
313
|
+
>${post.date}</span
|
|
314
|
+
>
|
|
315
|
+
</div>
|
|
316
|
+
<div
|
|
317
|
+
style="display: flex; flex-direction: column; gap: var(--g-theme-spacing-sm);"
|
|
318
|
+
>
|
|
319
|
+
<h3
|
|
320
|
+
style="margin: 0; font-size: var(--g-theme-typography-title-default-font-size); font-weight: var(--g-theme-typography-title-default-font-weight); color: var(--g-theme-color-content-default); line-height: 1.3;"
|
|
321
|
+
>
|
|
322
|
+
<a
|
|
323
|
+
href="${post.href}"
|
|
324
|
+
style="color: inherit; text-decoration: none;"
|
|
325
|
+
>${post.title}</a
|
|
326
|
+
>
|
|
327
|
+
</h3>
|
|
328
|
+
<p
|
|
329
|
+
style="margin: 0; color: var(--g-theme-color-content-secondary); font-size: var(--g-theme-typography-body-sm-font-size); line-height: 1.6;"
|
|
330
|
+
>
|
|
331
|
+
${post.excerpt}
|
|
332
|
+
</p>
|
|
333
|
+
</div>
|
|
334
|
+
<div
|
|
335
|
+
slot="footer"
|
|
336
|
+
style="display: flex; align-items: center; gap: var(--g-theme-spacing-sm);"
|
|
337
|
+
>
|
|
338
|
+
<grantcodes-avatar
|
|
339
|
+
src="${post.author.avatar}"
|
|
340
|
+
name="${post.author.name}"
|
|
341
|
+
size="small"
|
|
342
|
+
></grantcodes-avatar>
|
|
343
|
+
<span
|
|
344
|
+
style="font-size: var(--g-theme-typography-label-sm-font-size); color: var(--g-theme-color-content-secondary);"
|
|
345
|
+
>${post.author.name}</span
|
|
346
|
+
>
|
|
347
|
+
</div>
|
|
348
|
+
</grantcodes-card>
|
|
349
|
+
`,
|
|
350
|
+
)}
|
|
351
|
+
</div>
|
|
352
|
+
</grantcodes-container>
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<grantcodes-cta
|
|
356
|
+
eyebrow="Ready to try it?"
|
|
357
|
+
title="See how Flowbase keeps your team in flow"
|
|
358
|
+
text="Join thousands of teams who've replaced their meeting-heavy workflows with a calmer, more productive way of working."
|
|
359
|
+
primary-action=${JSON.stringify({
|
|
360
|
+
label: "Start for free",
|
|
361
|
+
href: "/signup",
|
|
362
|
+
})}
|
|
363
|
+
secondary-action=${JSON.stringify({
|
|
364
|
+
label: "See all features",
|
|
365
|
+
href: "/features",
|
|
366
|
+
})}
|
|
367
|
+
align="center"
|
|
368
|
+
></grantcodes-cta>
|
|
369
|
+
|
|
370
|
+
<grantcodes-footer columns="3">
|
|
371
|
+
${footerContent}
|
|
372
|
+
<div slot="bottom">
|
|
373
|
+
<p>© 2025 Flowbase, Inc. All rights reserved.</p>
|
|
374
|
+
</div>
|
|
375
|
+
<div slot="bottom" style="display: flex; gap: var(--g-theme-spacing-md);">
|
|
376
|
+
<a href="/privacy">Privacy</a>
|
|
377
|
+
<a href="/terms">Terms</a>
|
|
378
|
+
</div>
|
|
379
|
+
</grantcodes-footer>
|
|
380
|
+
`,
|
|
381
|
+
};
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import "../components/app-bar/app-bar.js";
|
|
3
|
+
import "../components/button/button.js";
|
|
4
|
+
import "../components/hero/hero.js";
|
|
5
|
+
import "../components/logo-cloud/logo-cloud.js";
|
|
6
|
+
import "../components/feature-list/feature-list.js";
|
|
7
|
+
import "../components/stats/stats.js";
|
|
8
|
+
import "../components/testimonials/testimonials.js";
|
|
9
|
+
import "../components/pricing/pricing.js";
|
|
10
|
+
import "../components/newsletter/newsletter.js";
|
|
11
|
+
import "../components/footer/footer.js";
|
|
12
|
+
|
|
13
|
+
const meta = {
|
|
14
|
+
title: "Pages/SaaS Landing",
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: "fullscreen",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
|
|
22
|
+
const navLinks = html`
|
|
23
|
+
<a href="/">Home</a>
|
|
24
|
+
<a href="/features">Features</a>
|
|
25
|
+
<a href="/pricing">Pricing</a>
|
|
26
|
+
<a href="/about">About</a>
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const footerContent = html`
|
|
30
|
+
<grantcodes-footer-column>
|
|
31
|
+
<h3>Flowbase</h3>
|
|
32
|
+
<p>The modern platform for teams that move fast and ship great software.</p>
|
|
33
|
+
</grantcodes-footer-column>
|
|
34
|
+
|
|
35
|
+
<grantcodes-footer-column>
|
|
36
|
+
<h3>Product</h3>
|
|
37
|
+
<ul>
|
|
38
|
+
<li><a href="/features">Features</a></li>
|
|
39
|
+
<li><a href="/pricing">Pricing</a></li>
|
|
40
|
+
<li><a href="/changelog">Changelog</a></li>
|
|
41
|
+
<li><a href="/roadmap">Roadmap</a></li>
|
|
42
|
+
</ul>
|
|
43
|
+
</grantcodes-footer-column>
|
|
44
|
+
|
|
45
|
+
<grantcodes-footer-column>
|
|
46
|
+
<h3>Company</h3>
|
|
47
|
+
<ul>
|
|
48
|
+
<li><a href="/about">About</a></li>
|
|
49
|
+
<li><a href="/blog">Blog</a></li>
|
|
50
|
+
<li><a href="/careers">Careers</a></li>
|
|
51
|
+
<li><a href="/press">Press</a></li>
|
|
52
|
+
</ul>
|
|
53
|
+
</grantcodes-footer-column>
|
|
54
|
+
|
|
55
|
+
<grantcodes-footer-column>
|
|
56
|
+
<h3>Legal</h3>
|
|
57
|
+
<ul>
|
|
58
|
+
<li><a href="/privacy">Privacy Policy</a></li>
|
|
59
|
+
<li><a href="/terms">Terms of Service</a></li>
|
|
60
|
+
<li><a href="/security">Security</a></li>
|
|
61
|
+
<li><a href="/cookies">Cookie Policy</a></li>
|
|
62
|
+
</ul>
|
|
63
|
+
</grantcodes-footer-column>
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Full SaaS product landing page. Demonstrates a realistic composition of
|
|
68
|
+
* all block components from top to bottom: app-bar, hero, logo-cloud,
|
|
69
|
+
* feature-list, stats, testimonials, pricing, newsletter, and footer.
|
|
70
|
+
*/
|
|
71
|
+
export const Default = {
|
|
72
|
+
render: () => html`
|
|
73
|
+
<grantcodes-app-bar sticky>
|
|
74
|
+
<a slot="logo" href="/" style="font-weight: 700; font-size: 1.25rem; text-decoration: none; color: inherit;">
|
|
75
|
+
Flowbase
|
|
76
|
+
</a>
|
|
77
|
+
<div slot="nav" style="display: flex; gap: 0.5rem;">
|
|
78
|
+
${navLinks}
|
|
79
|
+
</div>
|
|
80
|
+
<div slot="actions" style="display: flex; gap: 0.5rem;">
|
|
81
|
+
<grantcodes-button variant="ghost">Sign in</grantcodes-button>
|
|
82
|
+
<grantcodes-button>Get started</grantcodes-button>
|
|
83
|
+
</div>
|
|
84
|
+
</grantcodes-app-bar>
|
|
85
|
+
|
|
86
|
+
<grantcodes-hero
|
|
87
|
+
title="Ship faster without the chaos"
|
|
88
|
+
subtitle="Flowbase brings your team's tasks, docs, and deploys into one calm, focused workspace. No more tab-switching. No more dropped balls."
|
|
89
|
+
button="Start for free"
|
|
90
|
+
href="/signup"
|
|
91
|
+
size="lg"
|
|
92
|
+
></grantcodes-hero>
|
|
93
|
+
|
|
94
|
+
<grantcodes-logo-cloud
|
|
95
|
+
title="Trusted by teams at"
|
|
96
|
+
logos=${JSON.stringify([
|
|
97
|
+
{
|
|
98
|
+
name: "Vercel",
|
|
99
|
+
src: "https://placehold.co/120x40?text=Vercel",
|
|
100
|
+
alt: "Vercel",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "Linear",
|
|
104
|
+
src: "https://placehold.co/120x40?text=Linear",
|
|
105
|
+
alt: "Linear",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "Stripe",
|
|
109
|
+
src: "https://placehold.co/120x40?text=Stripe",
|
|
110
|
+
alt: "Stripe",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "Notion",
|
|
114
|
+
src: "https://placehold.co/120x40?text=Notion",
|
|
115
|
+
alt: "Notion",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "Figma",
|
|
119
|
+
src: "https://placehold.co/120x40?text=Figma",
|
|
120
|
+
alt: "Figma",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "Loom",
|
|
124
|
+
src: "https://placehold.co/120x40?text=Loom",
|
|
125
|
+
alt: "Loom",
|
|
126
|
+
},
|
|
127
|
+
])}
|
|
128
|
+
></grantcodes-logo-cloud>
|
|
129
|
+
|
|
130
|
+
<grantcodes-feature-list
|
|
131
|
+
title="Everything your team needs"
|
|
132
|
+
subtitle="Flowbase combines the tools you already love into a single, cohesive experience — so your team stays in flow."
|
|
133
|
+
columns="3"
|
|
134
|
+
items=${JSON.stringify([
|
|
135
|
+
{
|
|
136
|
+
title: "Task management",
|
|
137
|
+
description:
|
|
138
|
+
"Create, assign, and track work across your entire team with a simple, powerful interface.",
|
|
139
|
+
icon: "check-square",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
title: "Real-time docs",
|
|
143
|
+
description:
|
|
144
|
+
"Write collaboratively without conflicts. Docs live next to the tasks they belong to.",
|
|
145
|
+
icon: "file-text",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
title: "CI/CD pipelines",
|
|
149
|
+
description:
|
|
150
|
+
"Trigger builds, run tests, and deploy to production without leaving your workspace.",
|
|
151
|
+
icon: "git-branch",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
title: "Team inbox",
|
|
155
|
+
description:
|
|
156
|
+
"Pull in Slack, email, and GitHub notifications into one shared inbox your team can triage together.",
|
|
157
|
+
icon: "inbox",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
title: "Analytics",
|
|
161
|
+
description:
|
|
162
|
+
"Understand velocity, cycle time, and deployment frequency with built-in engineering metrics.",
|
|
163
|
+
icon: "bar-chart-2",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
title: "Access controls",
|
|
167
|
+
description:
|
|
168
|
+
"Fine-grained roles and SSO keep your data secure without slowing your team down.",
|
|
169
|
+
icon: "shield",
|
|
170
|
+
},
|
|
171
|
+
])}
|
|
172
|
+
></grantcodes-feature-list>
|
|
173
|
+
|
|
174
|
+
<grantcodes-stats
|
|
175
|
+
title="Built for teams that care about output"
|
|
176
|
+
columns="4"
|
|
177
|
+
items=${JSON.stringify([
|
|
178
|
+
{
|
|
179
|
+
value: "12×",
|
|
180
|
+
label: "Faster shipping",
|
|
181
|
+
context: "compared to teams using 4+ separate tools",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
value: "98%",
|
|
185
|
+
label: "Uptime SLA",
|
|
186
|
+
context: "backed by our enterprise plan",
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
value: "50k+",
|
|
190
|
+
label: "Active teams",
|
|
191
|
+
context: "across 80 countries",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
value: "4.9 / 5",
|
|
195
|
+
label: "Customer rating",
|
|
196
|
+
context: "across G2, Capterra, and Product Hunt",
|
|
197
|
+
},
|
|
198
|
+
])}
|
|
199
|
+
></grantcodes-stats>
|
|
200
|
+
|
|
201
|
+
<grantcodes-testimonials
|
|
202
|
+
title="What teams are saying"
|
|
203
|
+
layout="cards"
|
|
204
|
+
items=${JSON.stringify([
|
|
205
|
+
{
|
|
206
|
+
quote:
|
|
207
|
+
"We cut our weekly planning meeting from 90 minutes to 15. Flowbase just… makes it obvious what needs to happen next.",
|
|
208
|
+
name: "Maya Okafor",
|
|
209
|
+
role: "Engineering Lead",
|
|
210
|
+
company: "Pulse Labs",
|
|
211
|
+
avatar: "https://i.pravatar.cc/80?img=47",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
quote:
|
|
215
|
+
"I've tried every project tool out there. Flowbase is the first one that didn't need a dedicated Ops person to keep it clean.",
|
|
216
|
+
name: "Sam Torres",
|
|
217
|
+
role: "CTO",
|
|
218
|
+
company: "Stackform",
|
|
219
|
+
avatar: "https://i.pravatar.cc/80?img=12",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
quote:
|
|
223
|
+
"The CI integration alone saved us four hours a week. The rest is just a bonus at this point.",
|
|
224
|
+
name: "Priya Nair",
|
|
225
|
+
role: "Senior Developer",
|
|
226
|
+
company: "Mintcode",
|
|
227
|
+
avatar: "https://i.pravatar.cc/80?img=32",
|
|
228
|
+
},
|
|
229
|
+
])}
|
|
230
|
+
></grantcodes-testimonials>
|
|
231
|
+
|
|
232
|
+
<grantcodes-pricing
|
|
233
|
+
title="Simple, honest pricing"
|
|
234
|
+
subtitle="No per-seat surprises. No feature gating. Pick the plan that fits your team size."
|
|
235
|
+
tiers=${JSON.stringify([
|
|
236
|
+
{
|
|
237
|
+
name: "Starter",
|
|
238
|
+
price: "Free",
|
|
239
|
+
period: "",
|
|
240
|
+
description: "For solo developers and small side projects.",
|
|
241
|
+
features: [
|
|
242
|
+
{ text: "Up to 3 team members", included: true },
|
|
243
|
+
{ text: "Unlimited tasks", included: true },
|
|
244
|
+
{ text: "5 GB storage", included: true },
|
|
245
|
+
{ text: "CI/CD pipelines", included: false },
|
|
246
|
+
{ text: "SSO", included: false },
|
|
247
|
+
{ text: "Priority support", included: false },
|
|
248
|
+
],
|
|
249
|
+
cta: { label: "Get started free", href: "/signup" },
|
|
250
|
+
highlighted: false,
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: "Team",
|
|
254
|
+
price: "$49",
|
|
255
|
+
period: "/ month",
|
|
256
|
+
description: "For growing teams that need to move fast.",
|
|
257
|
+
features: [
|
|
258
|
+
{ text: "Up to 20 team members", included: true },
|
|
259
|
+
{ text: "Unlimited tasks", included: true },
|
|
260
|
+
{ text: "50 GB storage", included: true },
|
|
261
|
+
{ text: "CI/CD pipelines", included: true },
|
|
262
|
+
{ text: "SSO", included: false },
|
|
263
|
+
{ text: "Priority support", included: false },
|
|
264
|
+
],
|
|
265
|
+
cta: { label: "Start free trial", href: "/signup?plan=team" },
|
|
266
|
+
highlighted: true,
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: "Enterprise",
|
|
270
|
+
price: "$149",
|
|
271
|
+
period: "/ month",
|
|
272
|
+
description: "For large teams with advanced security needs.",
|
|
273
|
+
features: [
|
|
274
|
+
{ text: "Unlimited team members", included: true },
|
|
275
|
+
{ text: "Unlimited tasks", included: true },
|
|
276
|
+
{ text: "500 GB storage", included: true },
|
|
277
|
+
{ text: "CI/CD pipelines", included: true },
|
|
278
|
+
{ text: "SSO", included: true },
|
|
279
|
+
{ text: "Priority support", included: true },
|
|
280
|
+
],
|
|
281
|
+
cta: { label: "Contact sales", href: "/contact" },
|
|
282
|
+
highlighted: false,
|
|
283
|
+
},
|
|
284
|
+
])}
|
|
285
|
+
></grantcodes-pricing>
|
|
286
|
+
|
|
287
|
+
<grantcodes-newsletter
|
|
288
|
+
title="Stay in the loop"
|
|
289
|
+
text="Get product updates, engineering articles, and early access to new features. One email a week, no spam."
|
|
290
|
+
button-label="Subscribe"
|
|
291
|
+
placeholder="you@company.com"
|
|
292
|
+
disclaimer="Unsubscribe any time. We never sell your data."
|
|
293
|
+
></grantcodes-newsletter>
|
|
294
|
+
|
|
295
|
+
<grantcodes-footer columns="4">
|
|
296
|
+
${footerContent}
|
|
297
|
+
<div slot="bottom">
|
|
298
|
+
<p>© 2025 Flowbase, Inc. All rights reserved.</p>
|
|
299
|
+
</div>
|
|
300
|
+
<div slot="bottom" style="display: flex; gap: var(--g-theme-spacing-md);">
|
|
301
|
+
<a href="/privacy">Privacy</a>
|
|
302
|
+
<a href="/terms">Terms</a>
|
|
303
|
+
<a href="/security">Security</a>
|
|
304
|
+
</div>
|
|
305
|
+
</grantcodes-footer>
|
|
306
|
+
`,
|
|
307
|
+
};
|