@stone-js/starters 0.8.3 → 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 (91) hide show
  1. package/basic-react-declarative/app/Application.tsx +22 -6
  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/public/logo.svg +16 -0
  5. package/basic-react-declarative/tests/Application.spec.ts +13 -4
  6. package/basic-react-imperative/app/Application.tsx +22 -6
  7. package/basic-react-imperative/assets/css/index.css +163 -18
  8. package/basic-react-imperative/package.json +8 -5
  9. package/basic-react-imperative/public/logo.svg +16 -0
  10. package/basic-react-imperative/tests/Application.spec.ts +15 -4
  11. package/basic-service-declarative/app/Application.ts +18 -3
  12. package/basic-service-declarative/package.json +7 -4
  13. package/basic-service-declarative/tests/Application.spec.ts +3 -2
  14. package/basic-service-imperative/app/Application.ts +18 -3
  15. package/basic-service-imperative/package.json +7 -4
  16. package/basic-service-imperative/tests/Application.spec.ts +3 -2
  17. package/continuum-showcase/LICENSE +21 -0
  18. package/continuum-showcase/app/Application.ts +45 -0
  19. package/continuum-showcase/app/layouts/MainLayout.tsx +13 -8
  20. package/continuum-showcase/app/pages/BlogPostPage.tsx +6 -3
  21. package/continuum-showcase/app/pages/HomePage.tsx +21 -8
  22. package/continuum-showcase/assets/css/index.css +198 -0
  23. package/continuum-showcase/assets/images/logo.svg +16 -0
  24. package/continuum-showcase/package.json +28 -10
  25. package/continuum-showcase/tests/HomePage.spec.tsx +41 -0
  26. package/continuum-showcase/tsconfig.json +33 -0
  27. package/continuum-showcase/vitest.config.ts +31 -0
  28. package/full-react-declarative/app/layout/AppLayout/AppLayout.tsx +1 -1
  29. package/full-react-declarative/app/layout/ErrorLayout/ErrorLayout.tsx +1 -1
  30. package/full-react-declarative/app/layout/SecurityLayout/SecurityLayout.tsx +1 -1
  31. package/full-react-declarative/app/middleware/AuthMiddleware.ts +1 -1
  32. package/full-react-declarative/app/pages/home/HomePage.tsx +22 -21
  33. package/full-react-declarative/assets/css/index.css +167 -0
  34. package/full-react-declarative/package.json +13 -10
  35. package/full-react-declarative/public/logo.svg +16 -0
  36. package/full-react-declarative/tests/AppLayout.spec.ts +51 -0
  37. package/full-react-declarative/tests/HomePage.spec.ts +38 -0
  38. package/full-react-imperative/app/components/LayoutHeader/LayoutHeader.tsx +1 -1
  39. package/full-react-imperative/app/components/PostWidget/PostWidget.tsx +1 -1
  40. package/full-react-imperative/app/components/PullToRefresh/PullToRefresh.css +10 -0
  41. package/full-react-imperative/app/components/PullToRefresh/PullToRefresh.tsx +66 -0
  42. package/full-react-imperative/app/layout/ErrorLayout/ErrorLayout.tsx +1 -1
  43. package/full-react-imperative/app/layout/SecurityLayout/SecurityLayout.tsx +1 -1
  44. package/full-react-imperative/app/middleware/AuthMiddleware.ts +1 -1
  45. package/full-react-imperative/app/pages/home/HomePage.tsx +32 -16
  46. package/full-react-imperative/assets/css/index.css +167 -0
  47. package/full-react-imperative/package.json +14 -12
  48. package/full-react-imperative/public/logo.svg +16 -0
  49. package/full-react-imperative/tests/HomePage.spec.ts +45 -0
  50. package/full-react-imperative/tests/LayoutHeader.spec.ts +39 -0
  51. package/full-service-declarative/app/handlers/WelcomeEventHandler.ts +47 -0
  52. package/full-service-declarative/app/middleware/AuthMiddleware.ts +1 -1
  53. package/full-service-declarative/package.json +12 -9
  54. package/full-service-declarative/tests/PostService.spec.ts +56 -0
  55. package/full-service-declarative/tests/WelcomeEventHandler.spec.ts +54 -0
  56. package/full-service-declarative/vitest.config.ts +1 -1
  57. package/full-service-imperative/app/handlers/WelcomeEventHandler.ts +51 -0
  58. package/full-service-imperative/app/middleware/AuthMiddleware.ts +1 -1
  59. package/full-service-imperative/package.json +12 -9
  60. package/full-service-imperative/tests/PostService.spec.ts +47 -0
  61. package/full-service-imperative/tests/WelcomeEventHandler.spec.ts +31 -0
  62. package/full-service-imperative/vitest.config.ts +1 -1
  63. package/package.json +1 -1
  64. package/standard-react-declarative/app/pages/welcome/WelcomePage.tsx +37 -4
  65. package/standard-react-declarative/assets/css/index.css +163 -18
  66. package/standard-react-declarative/package.json +11 -8
  67. package/standard-react-declarative/public/logo.svg +16 -0
  68. package/standard-react-declarative/tests/WelcomePage.spec.ts +71 -0
  69. package/standard-react-imperative/app/pages/welcome/WelcomePage.tsx +37 -4
  70. package/standard-react-imperative/assets/css/index.css +163 -18
  71. package/standard-react-imperative/package.json +11 -8
  72. package/standard-react-imperative/public/logo.svg +16 -0
  73. package/standard-react-imperative/tests/WelcomePage.spec.ts +53 -0
  74. package/standard-service-declarative/app/handlers/WelcomeEventHandler.ts +2 -2
  75. package/standard-service-declarative/app/services/WelcomeService.ts +28 -2
  76. package/standard-service-declarative/package.json +9 -6
  77. package/standard-service-declarative/tests/Application.spec.ts +100 -0
  78. package/standard-service-declarative/vitest.config.ts +1 -1
  79. package/standard-service-imperative/app/handlers/welcomeEventHandler.ts +2 -4
  80. package/standard-service-imperative/app/services/welcomeService.ts +28 -2
  81. package/standard-service-imperative/package.json +9 -6
  82. package/standard-service-imperative/tests/Application.spec.ts +84 -0
  83. package/standard-service-imperative/vitest.config.ts +1 -1
  84. package/basic-react-declarative/public/logo.png +0 -0
  85. package/basic-react-declarative/tests/__snapshots__/Application.spec.ts.snap +0 -3
  86. package/basic-react-imperative/public/logo.png +0 -0
  87. package/basic-react-imperative/tests/__snapshots__/Application.spec.ts.snap +0 -3
  88. package/full-react-declarative/assets/img/logo.png +0 -0
  89. package/full-react-declarative/public/logo.png +0 -0
  90. package/full-react-imperative/assets/img/logo.png +0 -0
  91. package/full-react-imperative/public/logo.png +0 -0
@@ -58,11 +58,11 @@ export class Application implements IPage<ReactIncomingEvent> {
58
58
  */
59
59
  head ({ event }: PageHeadContext): Promiseable<HeadContext> {
60
60
  return {
61
- title: `Hello ${String(event.get<string>('name', 'World'))}!`,
62
- description: 'This is a simple example of a React application using Stone.js',
61
+ title: `${String(event.get<string>('name', 'World'))} · Welcome to Stone.js`,
62
+ description: 'A universal Stone.js application. Write your domain once; Stone.js is the context that runs it anywhere.',
63
63
  metas: [
64
64
  { name: 'author', content: 'Stone.js' },
65
- { name: 'keywords', content: 'stonejs,js,react,example' }
65
+ { name: 'keywords', content: 'stonejs,continuum,react,universal,framework' }
66
66
  ]
67
67
  }
68
68
  }
@@ -74,9 +74,25 @@ export class Application implements IPage<ReactIncomingEvent> {
74
74
  */
75
75
  render ({ data }: PageRenderContext<ResponseData>): JSX.Element {
76
76
  return (
77
- <section className='container'>
78
- <h1 className='h1 text-center mt-64'>{data?.message}</h1>
79
- </section>
77
+ <main className='stone-welcome'>
78
+ <div className='glow' aria-hidden='true' />
79
+ <section className='hero'>
80
+ <img className='mark' src='/logo.svg' alt='Stone.js' width={104} height={104} />
81
+ <p className='eyebrow'>Welcome to</p>
82
+ <h1 className='title'>Stone.js</h1>
83
+ <p className='lead'>{data?.message}</p>
84
+ <p className='tagline'>
85
+ Your app is running. Write your domain once, Stone.js is the context that runs it
86
+ anywhere: server, serverless, browser, CLI and the edge.
87
+ </p>
88
+ <nav className='links'>
89
+ <a href='https://stonejs.dev/docs' target='_blank' rel='noreferrer noopener'>Documentation</a>
90
+ <a href='https://github.com/stone-foundation/stone-js-framework' target='_blank' rel='noreferrer noopener'>GitHub</a>
91
+ <span className='edit'>Edit <b>app/Application.tsx</b></span>
92
+ </nav>
93
+ </section>
94
+ <footer className='brand'><span className='dot'>●</span> Stone.js — the continuum framework</footer>
95
+ </main>
80
96
  )
81
97
  }
82
98
  }
@@ -1,26 +1,171 @@
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; }
23
171
  }
24
- .mt-64 {
25
- margin-top: 64px;
26
- }
@@ -20,20 +20,23 @@
20
20
  "dependencies": {
21
21
  "react": "^19.0.0",
22
22
  "react-dom": "^19.0.0",
23
- "@stone-js/core": "^0.8.3",
24
- "@stone-js/use-react": "^0.8.3",
25
- "@stone-js/browser-adapter": "^0.8.3",
26
- "@stone-js/node-cli-adapter": "^0.8.3"
23
+ "@stone-js/core": "^0.8.5",
24
+ "@stone-js/use-react": "^0.8.5",
25
+ "@stone-js/browser-adapter": "^0.8.5",
26
+ "@stone-js/node-cli-adapter": "^0.8.5"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@babel/plugin-proposal-decorators": "^7.25.9",
30
30
  "@babel/preset-env": "^7.26.9",
31
31
  "@babel/preset-typescript": "^7.27.0",
32
- "@stone-js/cli": "^0.8.3",
32
+ "@stone-js/cli": "^0.8.5",
33
33
  "@types/node": "^24.0.7",
34
34
  "@types/react": "^19.0.7",
35
35
  "@types/react-dom": "^19.0.3",
36
36
  "@vitest/coverage-v8": "^3.0.9",
37
37
  "vitest": "^3.0.9"
38
+ },
39
+ "overrides": {
40
+ "glob": "^11.0.0"
38
41
  }
39
42
  }
@@ -0,0 +1,16 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="96" height="96" role="img" aria-label="Stone.js">
2
+ <title>Stone.js</title>
3
+ <style>
4
+ .ink { stroke: #1B1613; }
5
+ @media (prefers-color-scheme: dark) { .ink { stroke: #EFE8DC; } }
6
+ </style>
7
+ <defs>
8
+ <linearGradient id="braise" gradientUnits="userSpaceOnUse" x1="22" y1="14" x2="74" y2="26.1">
9
+ <stop offset="0" stop-color="#FFC46B" />
10
+ <stop offset="1" stop-color="#FF5A1F" />
11
+ </linearGradient>
12
+ </defs>
13
+ <path class="ink" d="M 79.96 36.4 A 34 34 0 0 1 53.9 81.5" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
14
+ <path class="ink" d="M 42.1 81.5 A 34 34 0 0 1 16.05 36.4" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
15
+ <path d="M 22 26.1 A 34 34 0 0 1 74 26.1" fill="none" stroke="url(#braise)" stroke-width="11" stroke-linecap="round" />
16
+ </svg>
@@ -58,7 +58,6 @@ describe('Application', () => {
58
58
 
59
59
  it('get head values', async () => {
60
60
  // Arrange
61
- const expectedMessage = 'Hello World!'
62
61
  const event = { get: () => 'World' } as unknown as ReactIncomingEvent
63
62
 
64
63
  // Act
@@ -66,7 +65,8 @@ describe('Application', () => {
66
65
 
67
66
  // Assert
68
67
  expect(head).toHaveProperty('metas')
69
- expect(head.title).toBe(expectedMessage)
68
+ expect(head.title).toContain('Welcome to Stone.js')
69
+ expect(head.title).toContain('World')
70
70
  expect(head).toHaveProperty('description')
71
71
  })
72
72
 
@@ -83,7 +83,7 @@ describe('Application', () => {
83
83
  expect(mockedLogger.info).toHaveBeenCalledWith(expectedMessage)
84
84
  })
85
85
 
86
- it('should render component', () => {
86
+ it('should render the message', () => {
87
87
  // Arrange
88
88
  const message = 'Hello World!'
89
89
 
@@ -91,6 +91,15 @@ describe('Application', () => {
91
91
  const response = renderToString(app.render({ data: { message } } as any))
92
92
 
93
93
  // Assert
94
- expect(response).toMatchSnapshot()
94
+ expect(response).toContain(message)
95
+ })
96
+
97
+ it('should render the Stone.js logo', () => {
98
+ // Act
99
+ const response = renderToString(app.render({ data: { message: 'Hello World!' } } as any))
100
+
101
+ // Assert
102
+ expect(response).toContain('/logo.svg')
103
+ expect(response).toContain('alt="Stone.js"')
95
104
  })
96
105
  })
@@ -27,11 +27,11 @@ export const FactoryHandler = ({ logger }: AppOptions): IPage<ReactIncomingEvent
27
27
  */
28
28
  head ({ event }: PageHeadContext): Promiseable<HeadContext> {
29
29
  return {
30
- title: `Hello ${String(event.get<string>('name', 'World'))}!`,
31
- description: 'This is a simple example of a React application using Stone.js',
30
+ title: `${String(event.get<string>('name', 'World'))} · Welcome to Stone.js`,
31
+ description: 'A universal Stone.js application. Write your domain once; Stone.js is the context that runs it anywhere.',
32
32
  metas: [
33
33
  { name: 'author', content: 'Stone.js' },
34
- { name: 'keywords', content: 'stonejs,js,react,example' }
34
+ { name: 'keywords', content: 'stonejs,continuum,react,universal,framework' }
35
35
  ]
36
36
  }
37
37
  },
@@ -43,9 +43,25 @@ export const FactoryHandler = ({ logger }: AppOptions): IPage<ReactIncomingEvent
43
43
  */
44
44
  render ({ data }: PageRenderContext<ResponseData>): JSX.Element {
45
45
  return (
46
- <section className='container'>
47
- <h1 className='h1 text-center mt-64'>{data?.message}</h1>
48
- </section>
46
+ <main className='stone-welcome'>
47
+ <div className='glow' aria-hidden='true' />
48
+ <section className='hero'>
49
+ <img className='mark' src='/logo.svg' alt='Stone.js' width={104} height={104} />
50
+ <p className='eyebrow'>Welcome to</p>
51
+ <h1 className='title'>Stone.js</h1>
52
+ <p className='lead'>{data?.message}</p>
53
+ <p className='tagline'>
54
+ Your app is running. Write your domain once, Stone.js is the context that runs it
55
+ anywhere: server, serverless, browser, CLI and the edge.
56
+ </p>
57
+ <nav className='links'>
58
+ <a href='https://stonejs.dev/docs' target='_blank' rel='noreferrer noopener'>Documentation</a>
59
+ <a href='https://github.com/stone-foundation/stone-js-framework' target='_blank' rel='noreferrer noopener'>GitHub</a>
60
+ <span className='edit'>Edit <b>app/Application.tsx</b></span>
61
+ </nav>
62
+ </section>
63
+ <footer className='brand'><span className='dot'>●</span> Stone.js — the continuum framework</footer>
64
+ </main>
49
65
  )
50
66
  }
51
67
  }
@@ -1,26 +1,171 @@
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; }
23
171
  }
24
- .mt-64 {
25
- margin-top: 64px;
26
- }
@@ -20,20 +20,23 @@
20
20
  "dependencies": {
21
21
  "react": "^19.0.0",
22
22
  "react-dom": "^19.0.0",
23
- "@stone-js/core": "^0.8.3",
24
- "@stone-js/use-react": "^0.8.3",
25
- "@stone-js/browser-adapter": "^0.8.3",
26
- "@stone-js/node-cli-adapter": "^0.8.3"
23
+ "@stone-js/core": "^0.8.5",
24
+ "@stone-js/use-react": "^0.8.5",
25
+ "@stone-js/browser-adapter": "^0.8.5",
26
+ "@stone-js/node-cli-adapter": "^0.8.5"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@babel/plugin-proposal-decorators": "^7.25.9",
30
30
  "@babel/preset-env": "^7.26.9",
31
31
  "@babel/preset-typescript": "^7.27.0",
32
- "@stone-js/cli": "^0.8.3",
32
+ "@stone-js/cli": "^0.8.5",
33
33
  "@types/node": "^24.0.7",
34
34
  "@types/react": "^19.0.7",
35
35
  "@types/react-dom": "^19.0.3",
36
36
  "@vitest/coverage-v8": "^3.0.9",
37
37
  "vitest": "^3.0.9"
38
+ },
39
+ "overrides": {
40
+ "glob": "^11.0.0"
38
41
  }
39
42
  }
@@ -0,0 +1,16 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="96" height="96" role="img" aria-label="Stone.js">
2
+ <title>Stone.js</title>
3
+ <style>
4
+ .ink { stroke: #1B1613; }
5
+ @media (prefers-color-scheme: dark) { .ink { stroke: #EFE8DC; } }
6
+ </style>
7
+ <defs>
8
+ <linearGradient id="braise" gradientUnits="userSpaceOnUse" x1="22" y1="14" x2="74" y2="26.1">
9
+ <stop offset="0" stop-color="#FFC46B" />
10
+ <stop offset="1" stop-color="#FF5A1F" />
11
+ </linearGradient>
12
+ </defs>
13
+ <path class="ink" d="M 79.96 36.4 A 34 34 0 0 1 53.9 81.5" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
14
+ <path class="ink" d="M 42.1 81.5 A 34 34 0 0 1 16.05 36.4" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
15
+ <path d="M 22 26.1 A 34 34 0 0 1 74 26.1" fill="none" stroke="url(#braise)" stroke-width="11" stroke-linecap="round" />
16
+ </svg>
@@ -30,7 +30,6 @@ describe('Application', () => {
30
30
 
31
31
  it('get head values', async () => {
32
32
  // Arrange
33
- const expectedMessage = 'Hello World!'
34
33
  const event = { get: () => 'World' } as unknown as IncomingEvent
35
34
 
36
35
  // Act
@@ -38,7 +37,8 @@ describe('Application', () => {
38
37
 
39
38
  // Assert
40
39
  expect(head).toHaveProperty('metas')
41
- expect(head.title).toBe(expectedMessage)
40
+ expect(head.title).toContain('Welcome to Stone.js')
41
+ expect(head.title).toContain('World')
42
42
  expect(head).toHaveProperty('description')
43
43
  })
44
44
 
@@ -55,7 +55,7 @@ describe('Application', () => {
55
55
  expect(mockedLogger.info).toHaveBeenCalledWith(expectedMessage)
56
56
  })
57
57
 
58
- it('should render component', () => {
58
+ it('should render the message', () => {
59
59
  // Arrange
60
60
  const message = 'Hello World!'
61
61
 
@@ -63,7 +63,18 @@ describe('Application', () => {
63
63
  const response = renderToString(app.render({ data: { message } } as any))
64
64
 
65
65
  // Assert
66
- expect(response).toMatchSnapshot()
66
+ expect(response).toContain(message)
67
+ })
68
+
69
+ it('should render the Stone.js logo', () => {
70
+ // Act
71
+ const response = renderToString(app.render({ data: { message: 'Hello World!' } } as any))
72
+
73
+ // Assert
74
+ expect(response).toContain('/logo.svg')
75
+ expect(response).toContain('alt="Stone.js"')
76
+ expect(response).toContain('Welcome to')
77
+ expect(response).toContain('Stone.js')
67
78
  })
68
79
 
69
80
  it('should get config', () => {