@stone-js/starters 0.8.4 → 0.8.5

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 (55) hide show
  1. package/basic-react-declarative/app/Application.tsx +22 -7
  2. package/basic-react-declarative/assets/css/index.css +163 -18
  3. package/basic-react-declarative/package.json +8 -5
  4. package/basic-react-declarative/tests/Application.spec.ts +2 -2
  5. package/basic-react-imperative/app/Application.tsx +22 -7
  6. package/basic-react-imperative/assets/css/index.css +163 -18
  7. package/basic-react-imperative/package.json +8 -5
  8. package/basic-react-imperative/tests/Application.spec.ts +4 -2
  9. package/basic-service-declarative/app/Application.ts +18 -3
  10. package/basic-service-declarative/package.json +7 -4
  11. package/basic-service-declarative/tests/Application.spec.ts +3 -2
  12. package/basic-service-imperative/app/Application.ts +18 -3
  13. package/basic-service-imperative/package.json +7 -4
  14. package/basic-service-imperative/tests/Application.spec.ts +3 -2
  15. package/continuum-showcase/app/layouts/MainLayout.tsx +13 -7
  16. package/continuum-showcase/app/pages/BlogPostPage.tsx +5 -2
  17. package/continuum-showcase/app/pages/HomePage.tsx +19 -6
  18. package/continuum-showcase/assets/css/index.css +190 -18
  19. package/continuum-showcase/package.json +14 -8
  20. package/continuum-showcase/tests/HomePage.spec.tsx +4 -3
  21. package/full-react-declarative/app/middleware/AuthMiddleware.ts +1 -1
  22. package/full-react-declarative/app/pages/home/HomePage.tsx +22 -21
  23. package/full-react-declarative/assets/css/index.css +167 -0
  24. package/full-react-declarative/package.json +13 -10
  25. package/full-react-declarative/tests/HomePage.spec.ts +38 -0
  26. package/full-react-imperative/app/middleware/AuthMiddleware.ts +1 -1
  27. package/full-react-imperative/app/pages/home/HomePage.tsx +32 -16
  28. package/full-react-imperative/assets/css/index.css +167 -0
  29. package/full-react-imperative/package.json +13 -10
  30. package/full-react-imperative/tests/HomePage.spec.ts +45 -0
  31. package/full-service-declarative/app/handlers/WelcomeEventHandler.ts +47 -0
  32. package/full-service-declarative/app/middleware/AuthMiddleware.ts +1 -1
  33. package/full-service-declarative/package.json +12 -9
  34. package/full-service-declarative/tests/WelcomeEventHandler.spec.ts +54 -0
  35. package/full-service-imperative/app/handlers/WelcomeEventHandler.ts +51 -0
  36. package/full-service-imperative/app/middleware/AuthMiddleware.ts +1 -1
  37. package/full-service-imperative/package.json +12 -9
  38. package/full-service-imperative/tests/WelcomeEventHandler.spec.ts +31 -0
  39. package/package.json +1 -1
  40. package/standard-react-declarative/app/pages/welcome/WelcomePage.tsx +37 -5
  41. package/standard-react-declarative/assets/css/index.css +163 -18
  42. package/standard-react-declarative/package.json +11 -8
  43. package/standard-react-declarative/tests/WelcomePage.spec.ts +2 -0
  44. package/standard-react-imperative/app/pages/welcome/WelcomePage.tsx +37 -5
  45. package/standard-react-imperative/assets/css/index.css +163 -18
  46. package/standard-react-imperative/package.json +11 -8
  47. package/standard-react-imperative/tests/WelcomePage.spec.ts +2 -0
  48. package/standard-service-declarative/app/handlers/WelcomeEventHandler.ts +2 -2
  49. package/standard-service-declarative/app/services/WelcomeService.ts +28 -2
  50. package/standard-service-declarative/package.json +9 -6
  51. package/standard-service-declarative/tests/Application.spec.ts +13 -4
  52. package/standard-service-imperative/app/handlers/welcomeEventHandler.ts +2 -4
  53. package/standard-service-imperative/app/services/welcomeService.ts +28 -2
  54. package/standard-service-imperative/package.json +9 -6
  55. package/standard-service-imperative/tests/Application.spec.ts +12 -3
@@ -8,13 +8,22 @@ import { defineStoneApp, defineConfig, IBlueprint, ILogger, IncomingEvent, Funct
8
8
  export const FactoryHandler = ({ logger }: AppOptions): FunctionalEventHandler<IncomingEvent, ResponseData> => {
9
9
  return (event: IncomingEvent): ResponseData => {
10
10
  // Get the name from the event
11
- const message = `Hello ${String(event.get<string>('name', 'World'))}!`
11
+ const name = String(event.get<string>('name', 'World'))
12
+ const message = `Hello ${name}! Welcome to Stone.js.`
12
13
 
13
14
  // Log a message
14
15
  logger.info(message)
15
16
 
16
- // Return a JSON response
17
- return { message }
17
+ // Return a branded JSON response
18
+ return {
19
+ message,
20
+ framework: {
21
+ name: 'Stone.js',
22
+ tagline: 'The continuum framework',
23
+ docs: 'https://stonejs.dev',
24
+ github: 'https://github.com/stone-foundation/stone-js-framework'
25
+ }
26
+ }
18
27
  }
19
28
  }
20
29
 
@@ -57,4 +66,10 @@ interface AppOptions {
57
66
  */
58
67
  export interface ResponseData {
59
68
  message: string
69
+ framework: {
70
+ name: string
71
+ tagline: string
72
+ docs: string
73
+ github: string
74
+ }
60
75
  }
@@ -18,17 +18,20 @@
18
18
  "test:cvg": "npm run test -- --coverage"
19
19
  },
20
20
  "dependencies": {
21
- "@stone-js/core": "^0.8.4",
22
- "@stone-js/node-cli-adapter": "^0.8.4",
23
- "@stone-js/node-http-adapter": "^0.8.4"
21
+ "@stone-js/core": "^0.8.5",
22
+ "@stone-js/node-cli-adapter": "^0.8.5",
23
+ "@stone-js/node-http-adapter": "^0.8.5"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@babel/plugin-proposal-decorators": "^7.25.9",
27
27
  "@babel/preset-env": "^7.26.9",
28
28
  "@babel/preset-typescript": "^7.27.0",
29
- "@stone-js/cli": "^0.8.4",
29
+ "@stone-js/cli": "^0.8.5",
30
30
  "@types/node": "^24.0.7",
31
31
  "@vitest/coverage-v8": "^3.0.9",
32
32
  "vitest": "^3.0.9"
33
+ },
34
+ "overrides": {
35
+ "glob": "^11.0.0"
33
36
  }
34
37
  }
@@ -34,8 +34,9 @@ describe('Application', () => {
34
34
  // Act
35
35
  const response = await app(event)
36
36
  // Assert
37
- expect(response.message).toBe('Hello World!')
38
- expect(mockedLogger.info).toHaveBeenCalledWith('Hello World!')
37
+ expect(response.message).toBe('Hello World! Welcome to Stone.js.')
38
+ expect(response.framework.name).toBe('Stone.js')
39
+ expect(mockedLogger.info).toHaveBeenCalledWith('Hello World! Welcome to Stone.js.')
39
40
  })
40
41
 
41
42
  it('should get config', () => {
@@ -1,18 +1,24 @@
1
1
  import React from 'react'
2
- import { PageLayout, IPageLayout, PageLayoutRenderContext } from '@stone-js/use-react'
2
+ import { PageLayout, StoneOutlet, IPageLayout, PageLayoutRenderContext } from '@stone-js/use-react'
3
3
 
4
4
  /**
5
- * The default layout. In a future iteration (hierarchical heads), a layout may also
6
- * contribute a base head via `createHead().merge(...)`.
5
+ * The default layout: a thin branded chrome (top bar + footer) around the page outlet.
6
+ * The page content is placed via <StoneOutlet>, never a bare {children}.
7
7
  */
8
8
  @PageLayout({ name: 'default' })
9
9
  export class MainLayout implements IPageLayout {
10
10
  render ({ children }: PageLayoutRenderContext) {
11
11
  return (
12
- <div className="app">
13
- <header><nav><a href="/">Home</a> · <a href="/blog/hello-world">Blog</a></nav></header>
14
- {children}
15
- <footer>◆ Stone.js — the continuum framework</footer>
12
+ <div className='cx-shell'>
13
+ <header className='cx-nav'>
14
+ <a className='cx-wordmark' href='/'><span className='dot'>●</span> Stone.js</a>
15
+ <nav>
16
+ <a href='/'>Home</a>
17
+ <a href='/blog/hello-world'>Blog</a>
18
+ </nav>
19
+ </header>
20
+ <StoneOutlet>{children}</StoneOutlet>
21
+ <footer className='cx-foot'>Built with Stone.js — the continuum framework</footer>
16
22
  </div>
17
23
  )
18
24
  }
@@ -20,7 +20,7 @@ interface Post {
20
20
  * Blog post page — demonstrates a fully dynamic, data-driven head built from the
21
21
  * loader (`handle`) output, including an `article` Open Graph type and per-post JSON-LD.
22
22
  */
23
- @Page({ path: '/blog/:slug' })
23
+ @Page('/blog/:slug')
24
24
  export class BlogPostPage implements IPage<ReactIncomingEvent> {
25
25
  handle (event: ReactIncomingEvent): Post {
26
26
  const slug = event.get<string>('slug', 'hello-world')
@@ -53,8 +53,11 @@ export class BlogPostPage implements IPage<ReactIncomingEvent> {
53
53
 
54
54
  render ({ data }: PageRenderContext<Post>) {
55
55
  return (
56
- <article>
56
+ <article className='cx-article'>
57
+ <a className='cx-back' href='/'>← Back</a>
58
+ <p className='cx-kicker'>Stone.js Blog</p>
57
59
  <h1>{data?.title}</h1>
60
+ <p className='cx-excerpt'>{data?.excerpt}</p>
58
61
  <p>{data?.body}</p>
59
62
  </article>
60
63
  )
@@ -18,7 +18,7 @@ interface HomeData {
18
18
  * Twitter card and JSON-LD structured data), all from `head()`;
19
19
  * - importing a static asset via the `@img` alias (resolved by the CLI's Vite config).
20
20
  */
21
- @Page({ path: '/' })
21
+ @Page('/')
22
22
  export class HomePage implements IPage<ReactIncomingEvent> {
23
23
  handle (): HomeData {
24
24
  return { message: 'Write your domain once. Stone.js applies the context.' }
@@ -48,11 +48,24 @@ export class HomePage implements IPage<ReactIncomingEvent> {
48
48
 
49
49
  render ({ data }: PageRenderContext<HomeData>) {
50
50
  return (
51
- <main>
52
- <img src={logo} alt="Stone.js" width={96} height={96} />
53
- <h1>Stone.js Showcase</h1>
54
- <p>{data?.message}</p>
55
- </main>
51
+ <section className='stone-welcome'>
52
+ <div className='glow' aria-hidden='true' />
53
+ <div className='hero'>
54
+ <img className='mark' src={logo} alt='Stone.js' width={104} height={104} />
55
+ <p className='eyebrow'>The continuum showcase</p>
56
+ <h1 className='title'>Stone.js</h1>
57
+ <p className='lead'>{data?.message}</p>
58
+ <p className='tagline'>
59
+ One domain, rendered on the server and hydrated in the browser, with a fluent
60
+ head / OpenGraph API and static-asset aliases.
61
+ </p>
62
+ <nav className='links'>
63
+ <a href='/blog/hello-world'>Read the blog demo</a>
64
+ <a href='https://stonejs.dev/docs' target='_blank' rel='noreferrer noopener'>Documentation</a>
65
+ <span className='edit'>Edit <b>app/pages/HomePage.tsx</b></span>
66
+ </nav>
67
+ </div>
68
+ </section>
56
69
  )
57
70
  }
58
71
  }
@@ -1,26 +1,198 @@
1
- body {
2
- margin: 0;
3
- padding: 0;
4
- color: #333;
5
- font-size: 14px;
6
- line-height: 1.5;
7
- background-color: #fff;
8
- font-family: 'Roboto', sans-serif;
1
+ /**
2
+ * Stone.js starter — brand welcome screen.
3
+ * Identity: "Obsidienne & Braise" — obsidian ground, ember (braise) accent, the Portal mark.
4
+ * Theme-aware: dark by default, light under prefers-color-scheme: light.
5
+ */
6
+ :root {
7
+ --ink: #14100E;
8
+ --ink-2: #1E1815;
9
+ --cream: #F4EFE6;
10
+ --cream-dim: #B8AE9E;
11
+ --ember-1: #FFC46B;
12
+ --ember-2: #FF5A1F;
13
+ --line: rgba(244, 239, 230, 0.10);
14
+ --card: rgba(244, 239, 230, 0.04);
15
+ --bg: radial-gradient(1200px 600px at 50% -10%, #241C17 0%, var(--ink) 55%);
9
16
  }
10
- .h1 {
17
+
18
+ @media (prefers-color-scheme: light) {
19
+ :root {
20
+ --ink: #F4EFE6;
21
+ --ink-2: #ffffff;
22
+ --cream: #1B1613;
23
+ --cream-dim: #6B6154;
24
+ --line: rgba(27, 22, 19, 0.10);
25
+ --card: rgba(27, 22, 19, 0.03);
26
+ --bg: radial-gradient(1200px 600px at 50% -10%, #FFFFFF 0%, #F1E9DC 60%);
27
+ }
28
+ }
29
+
30
+ * { box-sizing: border-box; }
31
+
32
+ body {
11
33
  margin: 0;
12
- font-size: 3rem;
13
- font-weight: 500;
14
- font-weight: bold;
34
+ min-height: 100vh;
35
+ color: var(--cream);
36
+ background: var(--ink);
37
+ font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
38
+ -webkit-font-smoothing: antialiased;
15
39
  }
16
- .container {
17
- width: 100vw;
40
+
41
+ .stone-welcome {
42
+ position: relative;
43
+ min-height: 100vh;
18
44
  display: flex;
45
+ flex-direction: column;
46
+ align-items: center;
19
47
  justify-content: center;
48
+ padding: 48px 24px;
49
+ background: var(--bg);
50
+ overflow: hidden;
51
+ }
52
+
53
+ /* Ember glow behind the mark. */
54
+ .stone-welcome .glow {
55
+ position: absolute;
56
+ top: 8%;
57
+ left: 50%;
58
+ width: 520px;
59
+ height: 520px;
60
+ transform: translateX(-50%);
61
+ background: radial-gradient(circle, rgba(255, 90, 31, 0.22) 0%, rgba(255, 90, 31, 0) 68%);
62
+ filter: blur(8px);
63
+ pointer-events: none;
64
+ z-index: 0;
20
65
  }
21
- .text-center {
66
+
67
+ .stone-welcome .hero {
68
+ position: relative;
69
+ z-index: 1;
70
+ width: 100%;
71
+ max-width: 680px;
22
72
  text-align: center;
73
+ animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both;
74
+ }
75
+
76
+ .stone-welcome .mark {
77
+ width: 104px;
78
+ height: 104px;
79
+ filter: drop-shadow(0 8px 28px rgba(255, 90, 31, 0.35));
80
+ }
81
+
82
+ .stone-welcome .eyebrow {
83
+ margin: 22px 0 0;
84
+ font-size: 12px;
85
+ font-weight: 600;
86
+ letter-spacing: 0.28em;
87
+ text-transform: uppercase;
88
+ color: var(--cream-dim);
89
+ }
90
+
91
+ .stone-welcome .title {
92
+ margin: 8px 0 0;
93
+ font-size: clamp(44px, 8vw, 76px);
94
+ font-weight: 800;
95
+ letter-spacing: -0.03em;
96
+ line-height: 1.02;
97
+ background: linear-gradient(92deg, var(--ember-1), var(--ember-2));
98
+ -webkit-background-clip: text;
99
+ background-clip: text;
100
+ color: transparent;
101
+ }
102
+
103
+ .stone-welcome .lead {
104
+ margin: 18px auto 0;
105
+ font-size: 20px;
106
+ font-weight: 600;
107
+ color: var(--cream);
108
+ }
109
+
110
+ .stone-welcome .tagline {
111
+ margin: 10px auto 0;
112
+ max-width: 46ch;
113
+ font-size: 16px;
114
+ line-height: 1.6;
115
+ color: var(--cream-dim);
116
+ }
117
+
118
+ .stone-welcome .links {
119
+ margin-top: 34px;
120
+ display: flex;
121
+ flex-wrap: wrap;
122
+ gap: 12px;
123
+ justify-content: center;
124
+ }
125
+
126
+ .stone-welcome .links a,
127
+ .stone-welcome .links .edit {
128
+ display: inline-flex;
129
+ align-items: center;
130
+ gap: 8px;
131
+ padding: 11px 18px;
132
+ border: 1px solid var(--line);
133
+ border-radius: 12px;
134
+ background: var(--card);
135
+ color: var(--cream);
136
+ font-size: 14px;
137
+ font-weight: 600;
138
+ text-decoration: none;
139
+ transition: border-color 0.2s, transform 0.2s, background 0.2s;
140
+ }
141
+
142
+ .stone-welcome .links a:hover {
143
+ transform: translateY(-2px);
144
+ border-color: rgba(255, 90, 31, 0.6);
145
+ background: rgba(255, 90, 31, 0.08);
146
+ }
147
+
148
+ .stone-welcome .links .edit {
149
+ font-family: ui-monospace, 'SF Mono', Menlo, Monaco, Consolas, monospace;
150
+ color: var(--cream-dim);
151
+ }
152
+ .stone-welcome .links .edit b { color: var(--ember-1); font-weight: 700; }
153
+
154
+ .stone-welcome .brand {
155
+ position: relative;
156
+ z-index: 1;
157
+ margin-top: 56px;
158
+ font-size: 13px;
159
+ letter-spacing: 0.02em;
160
+ color: var(--cream-dim);
161
+ }
162
+ .stone-welcome .brand .dot { color: var(--ember-2); }
163
+
164
+ @keyframes rise {
165
+ from { opacity: 0; transform: translateY(14px); }
166
+ to { opacity: 1; transform: translateY(0); }
167
+ }
168
+
169
+ @media (prefers-reduced-motion: reduce) {
170
+ .stone-welcome .hero { animation: none; }
171
+ }
172
+
173
+ /* --- Showcase chrome (MainLayout) --- */
174
+ .cx-shell { min-height: 100vh; display: flex; flex-direction: column; background: var(--bg); }
175
+ .cx-nav {
176
+ position: sticky; top: 0; z-index: 5;
177
+ display: flex; align-items: center; justify-content: space-between;
178
+ padding: 16px 24px; border-bottom: 1px solid var(--line);
179
+ backdrop-filter: blur(8px);
23
180
  }
24
- .mt-64 {
25
- margin-top: 64px;
26
- }
181
+ .cx-wordmark { font-weight: 800; letter-spacing: -0.02em; color: var(--cream); text-decoration: none; }
182
+ .cx-wordmark .dot { color: var(--ember-2); }
183
+ .cx-nav nav { display: flex; gap: 18px; }
184
+ .cx-nav nav a { color: var(--cream-dim); text-decoration: none; font-size: 14px; font-weight: 600; }
185
+ .cx-nav nav a:hover { color: var(--ember-1); }
186
+ .cx-foot { padding: 22px; text-align: center; color: var(--cream-dim); font-size: 13px; border-top: 1px solid var(--line); }
187
+
188
+ /* The hero fills the space between nav and footer rather than a full extra viewport. */
189
+ .cx-shell .stone-welcome { min-height: calc(100vh - 140px); }
190
+
191
+ /* --- Article (BlogPostPage) --- */
192
+ .cx-article { max-width: 720px; margin: 0 auto; padding: 64px 24px; color: var(--cream); }
193
+ .cx-back { color: var(--cream-dim); text-decoration: none; font-size: 14px; font-weight: 600; }
194
+ .cx-back:hover { color: var(--ember-1); }
195
+ .cx-kicker { margin: 28px 0 0; font-size: 12px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--ember-2); font-weight: 700; }
196
+ .cx-article h1 { margin: 8px 0 0; font-size: clamp(32px, 5vw, 48px); letter-spacing: -0.02em; }
197
+ .cx-excerpt { margin-top: 12px; font-size: 19px; color: var(--cream-dim); line-height: 1.6; }
198
+ .cx-article > p:last-child { margin-top: 20px; font-size: 16px; line-height: 1.75; }
@@ -15,23 +15,29 @@
15
15
  "dependencies": {
16
16
  "react": "^19.0.0",
17
17
  "react-dom": "^19.0.0",
18
- "@stone-js/core": "^0.8.4",
19
- "@stone-js/router": "^0.8.4",
20
- "@stone-js/use-react": "^0.8.4",
21
- "@stone-js/http-core": "^0.8.4",
22
- "@stone-js/pipeline": "^0.8.4",
23
- "@stone-js/browser-adapter": "^0.8.4",
24
- "@stone-js/node-http-adapter": "^0.8.4"
18
+ "@stone-js/core": "^0.8.5",
19
+ "@stone-js/router": "^0.8.5",
20
+ "@stone-js/use-react": "^0.8.5",
21
+ "@stone-js/http-core": "^0.8.5",
22
+ "@stone-js/pipeline": "^0.8.5",
23
+ "@stone-js/browser-adapter": "^0.8.5",
24
+ "@stone-js/node-http-adapter": "^0.8.5"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@babel/plugin-proposal-decorators": "^7.25.9",
28
28
  "@babel/preset-env": "^7.26.9",
29
29
  "@babel/preset-typescript": "^7.27.0",
30
- "@stone-js/cli": "^0.8.4",
30
+ "@stone-js/cli": "^0.8.5",
31
31
  "@types/node": "^24.0.7",
32
32
  "@types/react": "^19.0.7",
33
33
  "@types/react-dom": "^19.0.3",
34
34
  "@vitest/coverage-v8": "^3.0.9",
35
35
  "vitest": "^3.0.9"
36
+ },
37
+ "overrides": {
38
+ "glob": "^11.0.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=20.0.0"
36
42
  }
37
43
  }
@@ -29,12 +29,13 @@ describe('HomePage', () => {
29
29
  // Act
30
30
  const html = renderToString(page.render({ data: page.handle() } as any))
31
31
 
32
- // Assert: the Stone.js logo is rendered (Vite inlines the small SVG as a data URI),
33
- // alongside the title and the domain message.
32
+ // Assert: the premium welcome hero renders the Stone.js logo (Vite inlines the small SVG
33
+ // as a data URI), the brand title and the domain message.
34
+ expect(html).toContain('stone-welcome')
34
35
  expect(html).toContain('<img')
35
36
  expect(html).toContain('image/svg+xml')
36
37
  expect(html).toContain('alt="Stone.js"')
37
- expect(html).toContain('Stone.js Showcase')
38
+ expect(html).toContain('Stone.js')
38
39
  expect(html).toContain('Write your domain once')
39
40
  })
40
41
  })
@@ -18,7 +18,7 @@ export interface AuthMiddlewareOptions {
18
18
  */
19
19
  @Middleware({
20
20
  global: true,
21
- params: [['/login', '/register']]
21
+ params: [['/', '/login', '/register']]
22
22
  })
23
23
  export class AuthMiddleware implements IMiddleware<ReactIncomingEvent, ReactOutgoingResponse> {
24
24
  private readonly snapshot: ISnapshot
@@ -8,8 +8,8 @@ import { IPage, Page, ReactIncomingEvent, StoneLink, HeadContext } from '@stone-
8
8
  export class HomePage implements IPage<ReactIncomingEvent> {
9
9
  head (): HeadContext {
10
10
  return {
11
- title: 'Home',
12
- description: 'Welcome to the Stone.js Blog Dashboard'
11
+ title: 'Welcome to Stone.js',
12
+ description: 'Stone.js Blog Dashboard — write your domain once, Stone.js is the context that runs it anywhere.'
13
13
  }
14
14
  }
15
15
 
@@ -20,25 +20,26 @@ export class HomePage implements IPage<ReactIncomingEvent> {
20
20
  */
21
21
  render (): JSX.Element {
22
22
  return (
23
- <div>
24
- <h1>Stone.js Blog Dashboard</h1>
25
- <p>Welcome to the Stone.js Blog Dashboard.</p>
26
- <div>
27
- <h2>Posts</h2>
28
- <p>View the latest posts from the Stone.js Blog.</p>
29
- <StoneLink to='/posts'>View Posts</StoneLink>
30
- </div>
31
- <div>
32
- <h2>Users</h2>
33
- <p>View the latest users from the Stone.js Blog.</p>
34
- <StoneLink to='/users'>View Users</StoneLink>
35
- </div>
36
- <div>
37
- <h2>Manage</h2>
38
- <p>Manage your profile</p>
39
- <StoneLink to='/manage'>Manage</StoneLink>
40
- </div>
41
- </div>
23
+ <main className='stone-welcome'>
24
+ <div className='glow' aria-hidden='true' />
25
+ <section className='hero'>
26
+ <img className='mark' src='/logo.svg' alt='Stone.js' width={104} height={104} />
27
+ <p className='eyebrow'>Welcome to</p>
28
+ <h1 className='title'>Stone.js</h1>
29
+ <p className='lead'>Your Blog Dashboard is running.</p>
30
+ <p className='tagline'>
31
+ Write your domain once, Stone.js is the context that runs it anywhere:
32
+ server, serverless, browser, CLI and the edge.
33
+ </p>
34
+ <nav className='links'>
35
+ <StoneLink to='/posts'>Browse Posts</StoneLink>
36
+ <StoneLink to='/users'>Browse Users</StoneLink>
37
+ <a href='https://stonejs.dev/docs' target='_blank' rel='noreferrer noopener'>Documentation</a>
38
+ <a href='https://github.com/stone-foundation/stone-js-framework' target='_blank' rel='noreferrer noopener'>GitHub</a>
39
+ </nav>
40
+ </section>
41
+ <footer className='brand'><span className='dot'>●</span> Stone.js — the continuum framework</footer>
42
+ </main>
42
43
  )
43
44
  }
44
45
  }