@grantcodes/ui 2.0.2 → 2.1.1

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 (179) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/custom-elements.json +1926 -191
  3. package/package.json +22 -21
  4. package/src/components/accordion/accordion.component.js +33 -0
  5. package/src/components/accordion/accordion.js +6 -0
  6. package/src/components/accordion/accordion.stories.js +88 -0
  7. package/src/components/accordion/accordion.styles.js +66 -0
  8. package/src/components/accordion/index.js +6 -0
  9. package/src/components/app-bar/app-bar.component.js +1 -3
  10. package/src/components/app-bar/app-bar.js +0 -2
  11. package/src/components/app-bar/app-bar.styles.js +222 -221
  12. package/src/components/app-bar/app-bar.test.js +58 -17
  13. package/src/components/app-bar/index.js +0 -2
  14. package/src/components/avatar/avatar.js +0 -12
  15. package/src/components/avatar/avatar.stories.js +0 -12
  16. package/src/components/avatar/avatar.styles.js +19 -19
  17. package/src/components/avatar/avatar.test.js +4 -4
  18. package/src/components/avatar/index.js +1 -13
  19. package/src/components/badge/badge.js +0 -2
  20. package/src/components/badge/badge.styles.js +78 -81
  21. package/src/components/badge/badge.test.js +18 -5
  22. package/src/components/badge/index.js +0 -2
  23. package/src/components/breadcrumb/breadcrumb.component.js +9 -10
  24. package/src/components/breadcrumb/breadcrumb.js +6 -4
  25. package/src/components/breadcrumb/breadcrumb.styles.js +86 -90
  26. package/src/components/breadcrumb/breadcrumb.test.js +15 -5
  27. package/src/components/breadcrumb/index.js +0 -2
  28. package/src/components/button/button.component.js +2 -2
  29. package/src/components/button/button.styles.js +58 -86
  30. package/src/components/button/button.test.js +8 -4
  31. package/src/components/button/index.js +1 -1
  32. package/src/components/button-group/button-group.test.js +0 -2
  33. package/src/components/button-group/index.js +1 -1
  34. package/src/components/card/card.component.js +40 -9
  35. package/src/components/card/card.js +3 -1
  36. package/src/components/card/card.stories.js +18 -5
  37. package/src/components/card/card.styles.js +46 -20
  38. package/src/components/card/card.test.js +0 -2
  39. package/src/components/card/index.js +1 -1
  40. package/src/components/code-preview/code-preview.component.js +9 -9
  41. package/src/components/code-preview/code-preview.js +0 -1
  42. package/src/components/code-preview/code-preview.styles.js +3 -3
  43. package/src/components/code-preview/code-preview.test.js +29 -8
  44. package/src/components/code-preview/index.js +1 -1
  45. package/src/components/container/container.component.js +1 -0
  46. package/src/components/container/container.js +0 -1
  47. package/src/components/container/container.stories.js +12 -4
  48. package/src/components/container/container.styles.js +37 -35
  49. package/src/components/container/container.test.js +0 -2
  50. package/src/components/container/index.js +1 -1
  51. package/src/components/cta/cta.component.js +108 -0
  52. package/src/components/cta/cta.js +6 -0
  53. package/src/components/cta/cta.stories.js +56 -0
  54. package/src/components/cta/cta.styles.js +64 -0
  55. package/src/components/cta/index.js +1 -0
  56. package/src/components/dialog/dialog.js +0 -1
  57. package/src/components/dialog/dialog.styles.js +8 -8
  58. package/src/components/dialog/dialog.test.js +11 -5
  59. package/src/components/dialog/index.js +1 -1
  60. package/src/components/dropdown/dropdown.component.js +5 -3
  61. package/src/components/dropdown/dropdown.js +6 -4
  62. package/src/components/dropdown/dropdown.styles.js +5 -5
  63. package/src/components/dropdown/dropdown.test.js +20 -4
  64. package/src/components/dropdown/index.js +0 -2
  65. package/src/components/dropzone/dropzone.component.js +7 -6
  66. package/src/components/dropzone/dropzone.styles.js +4 -4
  67. package/src/components/dropzone/dropzone.test.js +6 -4
  68. package/src/components/dropzone/index.js +1 -1
  69. package/src/components/feature-list/feature-list.component.js +130 -0
  70. package/src/components/feature-list/feature-list.js +6 -0
  71. package/src/components/feature-list/feature-list.stories.js +117 -0
  72. package/src/components/feature-list/feature-list.styles.js +82 -0
  73. package/src/components/feature-list/index.js +1 -0
  74. package/src/components/footer/footer-column.styles.js +46 -47
  75. package/src/components/footer/footer.js +6 -2
  76. package/src/components/footer/footer.styles.js +6 -6
  77. package/src/components/footer/footer.test.js +9 -4
  78. package/src/components/footer/index.js +1 -1
  79. package/src/components/form-field/form-field.component.js +1 -3
  80. package/src/components/form-field/form-field.js +0 -1
  81. package/src/components/form-field/form-field.styles.js +35 -37
  82. package/src/components/form-field/form-field.test.js +9 -4
  83. package/src/components/form-field/index.js +1 -1
  84. package/src/components/gallery/gallery-image.js +0 -1
  85. package/src/components/gallery/gallery.js +0 -1
  86. package/src/components/gallery/gallery.styles.js +1 -1
  87. package/src/components/gallery/gallery.test.js +5 -3
  88. package/src/components/gallery/index.js +2 -2
  89. package/src/components/hero/hero.component.js +66 -0
  90. package/src/components/hero/hero.js +6 -0
  91. package/src/components/hero/hero.stories.js +53 -0
  92. package/src/components/hero/hero.styles.js +46 -0
  93. package/src/components/hero/index.js +1 -0
  94. package/src/components/icon/icon.js +3 -2
  95. package/src/components/icon/icon.stories.js +2 -1
  96. package/src/components/icon/icon.styles.js +23 -21
  97. package/src/components/icon/icon.test.js +2 -3
  98. package/src/components/icon/index.js +1 -1
  99. package/src/components/loading/index.js +1 -1
  100. package/src/components/loading/loading.js +3 -2
  101. package/src/components/loading/loading.styles.js +1 -1
  102. package/src/components/loading/loading.test.js +0 -2
  103. package/src/components/logo-cloud/index.js +1 -0
  104. package/src/components/logo-cloud/logo-cloud.component.js +81 -0
  105. package/src/components/logo-cloud/logo-cloud.js +6 -0
  106. package/src/components/logo-cloud/logo-cloud.stories.js +107 -0
  107. package/src/components/logo-cloud/logo-cloud.styles.js +68 -0
  108. package/src/components/media-text/index.js +1 -0
  109. package/src/components/media-text/media-text.component.js +100 -0
  110. package/src/components/media-text/media-text.js +6 -0
  111. package/src/components/media-text/media-text.stories.js +69 -0
  112. package/src/components/media-text/media-text.styles.js +66 -0
  113. package/src/components/newsletter/index.js +1 -0
  114. package/src/components/newsletter/newsletter.component.js +101 -0
  115. package/src/components/newsletter/newsletter.js +6 -0
  116. package/src/components/newsletter/newsletter.stories.js +59 -0
  117. package/src/components/newsletter/newsletter.styles.js +89 -0
  118. package/src/components/notice/index.js +1 -1
  119. package/src/components/notice/notice.js +0 -1
  120. package/src/components/notice/notice.styles.js +7 -7
  121. package/src/components/notice/notice.test.js +15 -5
  122. package/src/components/pagination/index.js +1 -1
  123. package/src/components/pagination/pagination.stories.js +1 -3
  124. package/src/components/pagination/pagination.styles.js +1 -1
  125. package/src/components/pagination/pagination.test.js +9 -4
  126. package/src/components/pricing/index.js +1 -0
  127. package/src/components/pricing/pricing.component.js +119 -0
  128. package/src/components/pricing/pricing.js +6 -0
  129. package/src/components/pricing/pricing.stories.js +123 -0
  130. package/src/components/pricing/pricing.styles.js +135 -0
  131. package/src/components/sidebar/index.js +0 -2
  132. package/src/components/sidebar/sidebar.component.js +12 -10
  133. package/src/components/sidebar/sidebar.js +3 -3
  134. package/src/components/sidebar/sidebar.stories.js +0 -2
  135. package/src/components/sidebar/sidebar.styles.js +181 -186
  136. package/src/components/sidebar/sidebar.test.js +48 -13
  137. package/src/components/stats/index.js +1 -0
  138. package/src/components/stats/stats.component.js +73 -0
  139. package/src/components/stats/stats.js +6 -0
  140. package/src/components/stats/stats.stories.js +64 -0
  141. package/src/components/stats/stats.styles.js +66 -0
  142. package/src/components/tabs/index.js +2 -2
  143. package/src/components/tabs/internal/tabs-button.component.js +1 -1
  144. package/src/components/tabs/internal/tabs-button.js +0 -1
  145. package/src/components/tabs/tab.js +0 -1
  146. package/src/components/tabs/tabs.js +3 -2
  147. package/src/components/tabs/tabs.styles.js +84 -74
  148. package/src/components/testimonials/index.js +1 -0
  149. package/src/components/testimonials/testimonials.component.js +97 -0
  150. package/src/components/testimonials/testimonials.js +6 -0
  151. package/src/components/testimonials/testimonials.stories.js +78 -0
  152. package/src/components/testimonials/testimonials.styles.js +82 -0
  153. package/src/components/toast/index.js +0 -2
  154. package/src/components/toast/toast.component.js +1 -3
  155. package/src/components/toast/toast.js +10 -5
  156. package/src/components/toast/toast.stories.js +9 -5
  157. package/src/components/toast/toast.styles.js +199 -201
  158. package/src/components/toast/toast.test.js +38 -10
  159. package/src/components/tooltip/index.js +1 -1
  160. package/src/components/tooltip/tooltip.js +3 -2
  161. package/src/components/tooltip/tooltip.styles.js +3 -3
  162. package/src/components/tooltip/tooltip.test.js +10 -4
  163. package/src/css/base.css +8 -5
  164. package/src/css/colors.stories.js +27 -28
  165. package/src/css/elements/forms/input.css +9 -41
  166. package/src/css/elements/media/image.css +1 -1
  167. package/src/css/themes/todomap.css +1 -0
  168. package/src/css/tokens.stories.js +26 -21
  169. package/src/css/typography.css +1 -3
  170. package/src/css/util/focus-ring.css +30 -0
  171. package/src/css/util/index.css +1 -2
  172. package/src/lib/styles/focus-ring.styles.js +34 -0
  173. package/src/main.js +10 -1
  174. package/src/pages/agency.stories.js +164 -0
  175. package/src/pages/blog-post.stories.js +381 -0
  176. package/src/pages/saas-landing.stories.js +307 -0
  177. package/src/test-utils/assert-helpers.js +10 -8
  178. package/src/css/util/functions.css +0 -16
  179. 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>&copy; 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>&copy; 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
+ };