create-thunderous 0.0.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.
@@ -0,0 +1,52 @@
1
+ <!-- Since this file begins with an underscore, it will be excluded from the output. -->
2
+ <!-- This markup will only be rendered if a page includes it with <?layout href="_layout.html"> -->
3
+ <html>
4
+ <head>
5
+ <title>{{ APP_NAME }}<script expr>title !== '' ? ` | ${title}` : ''</script></title>
6
+ <link rel="stylesheet" href="/global.css" />
7
+ </head>
8
+ <body>
9
+ <t-page>
10
+ <h1 slot="header">
11
+ <script expr>
12
+ title === '' ? 'Home' : title;
13
+ </script>
14
+ </h1>
15
+
16
+ <!-- We can use multiple views to render different content in different regions - see main-content for more details -->
17
+ <t-view id="breadcrumbs">
18
+ <nav slot="header">
19
+ <p>
20
+ <script expr>
21
+ breadcrumbs.map(
22
+ (crumb, index) => html`
23
+ <a href="${crumb.pathname}">${crumb.name}</a>
24
+ ${index < breadcrumbs.length - 1 ? html`<span> | </span>` : ''}
25
+ `,
26
+ );
27
+ </script>
28
+ </p>
29
+ </nav>
30
+ </t-view>
31
+
32
+ <!-- This view element is used for CSR navigation, only this element will be replaced when navigating -->
33
+ <t-view id="main-content">
34
+ <!-- This is where page content is inserted when this layout is used -->
35
+ <slot></slot>
36
+ </t-view>
37
+ </t-page>
38
+
39
+ <script isomorphic>
40
+ import { View } from 'thunderous-csr';
41
+ import { Page } from './components/page';
42
+ View.define('t-view');
43
+ Page.define('t-page');
44
+ </script>
45
+
46
+ <!-- This makes some handy data available to the template, like breadcrumbs -->
47
+ <script server>
48
+ import { getMeta } from 'thunderous-server';
49
+ export default getMeta();
50
+ </script>
51
+ </body>
52
+ </html>
@@ -0,0 +1,7 @@
1
+ // This file is excluded from client output by the *.server.ts convention;
2
+ // that is, any file ending with .server.ts will ONLY be evaluated on the server.
3
+ // This is useful to avoid inadvertently exposing sensitive data to the client.
4
+ export default {
5
+ teamName: 'Thunderous Team',
6
+ description: 'We build fast static sites.',
7
+ };
@@ -0,0 +1,6 @@
1
+ <!-- Notice this file is named contact.html, instead of nesting index.html in a folder called contact. -->
2
+ <!-- This just demonstrates that you CAN but don't NEED to nest index files in folders to create pages. -->
3
+ <!-- This directly reflects the native behavior of static sites. No complex route configs needed! -->
4
+ <?layout href="../_layout.html">
5
+
6
+ <p>Contact Us</p>
@@ -0,0 +1,16 @@
1
+ <!-- Since this is named index.html, it will be the default page for the /about route. -->
2
+ <?layout href="../_layout.html">
3
+
4
+ <h2>
5
+ <script expr>
6
+ teamName;
7
+ </script>
8
+ </h2>
9
+ <p>
10
+ <script expr>
11
+ description;
12
+ </script>
13
+ </p>
14
+
15
+ <!-- <script server> tags also support files via the href attribute -->
16
+ <script server href="./about-data.server.ts"></script>
@@ -0,0 +1,163 @@
1
+ import { customElement, html, css } from 'thunderous';
2
+
3
+ export const MyComponent = customElement(({ clientOnlyCallback, adoptStyleSheet }) => {
4
+ clientOnlyCallback(() => {
5
+ console.log('MyComponent has been mounted on the client side.');
6
+ });
7
+ adoptStyleSheet(stylesheet);
8
+ return html`
9
+ <div class="my-component">
10
+ <slot></slot>
11
+ <a href="https://thunderous.dev">Learn More</a>
12
+ </div>
13
+ `;
14
+ });
15
+
16
+ const stylesheet = css`
17
+ :host {
18
+ display: block;
19
+ font-size: 2rem;
20
+
21
+ --bg-1: rgba(255, 255, 255, 0.72);
22
+ --bg-2: rgba(255, 255, 255, 0.5);
23
+ --border: rgba(255, 255, 255, 0.35);
24
+ --text: #1e293b;
25
+ --muted: #b8c7e6;
26
+ --link: #7dd3fc;
27
+ --link-hover: #c4b5fd;
28
+ --shadow: 0 18px 50px rgba(15, 23, 42, 0.35);
29
+
30
+ @media (prefers-color-scheme: light) {
31
+ --bg-1: rgba(255, 255, 255, 0.88);
32
+ --bg-2: rgba(240, 249, 255, 0.74);
33
+ --border: rgba(99, 102, 241, 0.16);
34
+ --text: #1e293b;
35
+ --muted: #475569;
36
+ --link: #2563eb;
37
+ --link-hover: #7c3aed;
38
+ --shadow: 0 18px 40px rgba(51, 65, 85, 0.14);
39
+ }
40
+ }
41
+
42
+ ::slotted(*) {
43
+ margin: 0;
44
+ color: var(--text);
45
+ }
46
+
47
+ .my-component {
48
+ text-align: center;
49
+ position: relative;
50
+ overflow: hidden;
51
+ padding: 1.25em 1.5em;
52
+ border-radius: 20px;
53
+ border: 1px solid var(--border);
54
+ background:
55
+ linear-gradient(135deg, rgba(99, 102, 241, 0.22), rgba(14, 165, 233, 0.16)),
56
+ linear-gradient(180deg, var(--bg-1), var(--bg-2));
57
+ backdrop-filter: blur(18px) saturate(160%);
58
+ -webkit-backdrop-filter: blur(18px) saturate(160%);
59
+ box-shadow: var(--shadow);
60
+ isolation: isolate;
61
+ margin: 1em auto;
62
+ max-width: 20em;
63
+ }
64
+
65
+ .my-component::before,
66
+ .my-component::after {
67
+ content: '';
68
+ position: absolute;
69
+ inset: auto;
70
+ border-radius: 999px;
71
+ filter: blur(10px);
72
+ opacity: 0.8;
73
+ z-index: -1;
74
+ pointer-events: none;
75
+ }
76
+
77
+ .my-component::before {
78
+ top: -60px;
79
+ right: -40px;
80
+ width: 180px;
81
+ height: 180px;
82
+ background: radial-gradient(circle, rgba(125, 211, 252, 0.35), transparent 65%);
83
+ }
84
+
85
+ .my-component::after {
86
+ bottom: -70px;
87
+ left: -30px;
88
+ width: 160px;
89
+ height: 160px;
90
+ background: radial-gradient(circle, rgba(196, 181, 253, 0.28), transparent 65%);
91
+ }
92
+
93
+ .my-component p {
94
+ margin: 0;
95
+ font-size: 1rem;
96
+ line-height: 1.75;
97
+ letter-spacing: 0.01em;
98
+ color: var(--text);
99
+ text-wrap: pretty;
100
+ }
101
+
102
+ .my-component a {
103
+ display: inline-flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ gap: 0.5rem;
107
+ margin-top: 1rem;
108
+ padding: 0.85rem 1.25rem;
109
+ border-radius: 999px;
110
+ border: 1px solid color-mix(in srgb, var(--border) 85%, white 15%);
111
+ background:
112
+ linear-gradient(135deg, var(--link), var(--link-hover)), linear-gradient(180deg, var(--bg-1), var(--bg-2));
113
+ box-shadow:
114
+ 0 10px 30px color-mix(in srgb, var(--link-hover) 28%, transparent),
115
+ inset 0 1px 0 color-mix(in srgb, white 35%, transparent);
116
+ color: var(--text);
117
+ font-weight: 700;
118
+ line-height: 1;
119
+ letter-spacing: 0.01em;
120
+ text-decoration: none;
121
+ white-space: nowrap;
122
+ transition:
123
+ transform 180ms ease,
124
+ box-shadow 180ms ease,
125
+ filter 180ms ease,
126
+ border-color 180ms ease,
127
+ color 180ms ease;
128
+ }
129
+
130
+ .my-component a:hover,
131
+ .my-component a:focus-visible {
132
+ transform: translateY(-2px);
133
+ filter: brightness(1.05);
134
+ border-color: color-mix(in srgb, var(--link) 55%, var(--border));
135
+ box-shadow:
136
+ 0 16px 36px color-mix(in srgb, var(--link-hover) 36%, transparent),
137
+ inset 0 1px 0 color-mix(in srgb, white 45%, transparent);
138
+ color: var(--text);
139
+ }
140
+
141
+ .my-component a:active {
142
+ transform: translateY(0);
143
+ box-shadow:
144
+ 0 8px 20px color-mix(in srgb, var(--link-hover) 22%, transparent),
145
+ inset 0 1px 0 color-mix(in srgb, white 25%, transparent);
146
+ }
147
+
148
+ .my-component a:focus-visible {
149
+ outline: 2px solid color-mix(in srgb, var(--link) 65%, white 35%);
150
+ outline-offset: 4px;
151
+ }
152
+
153
+ .my-component a::after {
154
+ content: '→';
155
+ font-size: 0.95em;
156
+ transition: transform 180ms ease;
157
+ }
158
+
159
+ .my-component a:hover::after,
160
+ .my-component a:focus-visible::after {
161
+ transform: translateX(3px);
162
+ }
163
+ `;
@@ -0,0 +1,240 @@
1
+ import { customElement, html, css } from 'thunderous';
2
+
3
+ export const Page = customElement(
4
+ ({ adoptStyleSheet, propSignals }) => {
5
+ adoptStyleSheet(stylesheet);
6
+ const { prominent } = propSignals;
7
+ const prominentClass = prominent ? 'prominent' : '';
8
+ return html`
9
+ <div class="page ${prominentClass}">
10
+ <header>
11
+ <slot name="header"></slot>
12
+ <nav class="header-nav">
13
+ <a href="/">Home</a>
14
+ <a href="/about">About Us</a>
15
+ <a href="/about/contact">Contact Us</a>
16
+ </nav>
17
+ </header>
18
+ <main>
19
+ <slot></slot>
20
+ </main>
21
+ <footer>
22
+ <nav class="footer-nav">
23
+ <a href="/">Home</a> | <a href="/about">About Us</a> | <a href="/about/contact">Contact Us</a>
24
+ </nav>
25
+ <slot name="footer"></slot>
26
+ <small>&copy; ${new Date().getFullYear()} &mdash; Powered by <a href="https://thunderous.dev">Thunderous</a></small>
27
+ </footer>
28
+ </div>
29
+ `;
30
+ },
31
+ {
32
+ attributesAsProperties: [['prominent', Boolean]],
33
+ },
34
+ );
35
+
36
+ const stylesheet = css`
37
+ :host {
38
+ display: block;
39
+ height: 100%;
40
+ width: 100%;
41
+
42
+ --bg-1: rgba(255, 255, 255, 0.06);
43
+ --bg-2: rgba(255, 255, 255, 0.03);
44
+ --border: rgba(255, 255, 255, 0.1);
45
+ --text: #e5eefc;
46
+ --muted: #9fb0d1;
47
+ --link: #8fb4ff;
48
+ --link-hover: #2a4692;
49
+ --shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
50
+ }
51
+ .page {
52
+ display: grid;
53
+ grid-template-rows: auto 1fr auto;
54
+ box-sizing: border-box;
55
+ min-width: 320px;
56
+ min-height: 100vh;
57
+ background:
58
+ radial-gradient(circle at top left, rgba(99, 102, 241, 0.22), transparent 30%),
59
+ radial-gradient(circle at bottom right, rgba(14, 165, 233, 0.18), transparent 35%),
60
+ linear-gradient(180deg, #0f172a 0%, #111827 45%, #0b1120 100%);
61
+ color: #eaf2ff;
62
+ font-family: 'Plus Jakarta Sans', Inter, ui-sans-serif, system-ui, sans-serif;
63
+ font-size: 1rem;
64
+ line-height: 1.6;
65
+ font-weight: 400;
66
+ letter-spacing: 0.01em;
67
+ font-synthesis: none;
68
+ text-rendering: optimizeLegibility;
69
+ -moz-osx-font-smoothing: grayscale;
70
+ -webkit-font-smoothing: antialiased;
71
+ }
72
+ .page.prominent {
73
+ align-items: center;
74
+ }
75
+ .header-nav {
76
+ display: flex;
77
+ flex-wrap: wrap;
78
+ gap: 0.75rem;
79
+ align-items: center;
80
+ padding: 0.75rem 1rem;
81
+ border: 1px solid var(--border);
82
+ border-radius: 999px;
83
+ background: linear-gradient(
84
+ 135deg,
85
+ color-mix(in srgb, var(--bg-1) 88%, transparent),
86
+ color-mix(in srgb, var(--bg-2) 92%, transparent)
87
+ );
88
+ backdrop-filter: blur(18px) saturate(160%);
89
+ -webkit-backdrop-filter: blur(18px) saturate(160%);
90
+ box-shadow: var(--shadow);
91
+ }
92
+
93
+ .header-nav a {
94
+ position: relative;
95
+ display: inline-flex;
96
+ align-items: center;
97
+ justify-content: center;
98
+ padding: 0.7rem 1rem;
99
+ border-radius: 999px;
100
+ color: var(--text);
101
+ font-weight: 600;
102
+ line-height: 1;
103
+ letter-spacing: 0.01em;
104
+ text-decoration: none;
105
+ transition:
106
+ transform 180ms ease,
107
+ background 180ms ease,
108
+ box-shadow 180ms ease,
109
+ color 180ms ease;
110
+ }
111
+
112
+ .header-nav a::before {
113
+ content: '';
114
+ position: absolute;
115
+ inset: 0;
116
+ border-radius: inherit;
117
+ background: linear-gradient(135deg, var(--link), var(--link-hover));
118
+ opacity: 0;
119
+ transform: scale(0.96);
120
+ transition:
121
+ opacity 180ms ease,
122
+ transform 180ms ease;
123
+ z-index: -1;
124
+ }
125
+
126
+ .header-nav a:hover,
127
+ .header-nav a:focus-visible {
128
+ color: var(--text);
129
+ transform: translateY(-1px);
130
+ box-shadow:
131
+ 0 10px 24px color-mix(in srgb, var(--link-hover) 22%, transparent),
132
+ inset 0 1px 0 color-mix(in srgb, white 30%, transparent);
133
+ }
134
+
135
+ .header-nav a:hover::before,
136
+ .header-nav a:focus-visible::before {
137
+ opacity: 1;
138
+ transform: scale(1);
139
+ }
140
+
141
+ .header-nav a:active {
142
+ transform: translateY(0);
143
+ }
144
+
145
+ .header-nav a:focus-visible {
146
+ outline: 2px solid color-mix(in srgb, var(--link) 65%, white 35%);
147
+ outline-offset: 4px;
148
+ }
149
+
150
+ header {
151
+ padding: 1em 1.5em;
152
+ }
153
+ main {
154
+ padding: 3em 1.5em;
155
+ }
156
+
157
+ footer {
158
+ display: grid;
159
+ place-items: center;
160
+ gap: 1.5rem;
161
+ padding: 2rem 1.5rem;
162
+ border-top: 1px solid var(--border);
163
+ background: linear-gradient(180deg, var(--bg-2), transparent);
164
+ color: var(--muted);
165
+ }
166
+
167
+ .footer-nav {
168
+ display: flex;
169
+ flex-wrap: wrap;
170
+ gap: 0.5rem;
171
+ align-items: center;
172
+ padding: 0;
173
+ border: 0;
174
+ border-radius: 0;
175
+ background: none;
176
+ backdrop-filter: none;
177
+ -webkit-backdrop-filter: none;
178
+ box-shadow: none;
179
+ }
180
+
181
+ .footer-nav a {
182
+ position: relative;
183
+ display: inline-flex;
184
+ align-items: center;
185
+ padding: 0.35rem 0.2rem;
186
+ border-radius: 0.5rem;
187
+ color: var(--muted);
188
+ font-weight: 500;
189
+ text-decoration: none;
190
+ transition:
191
+ color 180ms ease,
192
+ background-color 180ms ease;
193
+ }
194
+
195
+ .footer-nav a::before {
196
+ content: none;
197
+ }
198
+
199
+ .footer-nav a:hover,
200
+ .footer-nav a:focus-visible {
201
+ color: var(--text);
202
+ background: color-mix(in srgb, var(--bg-1) 70%, transparent);
203
+ box-shadow: none;
204
+ transform: none;
205
+ }
206
+
207
+ .footer-nav a:active {
208
+ transform: none;
209
+ }
210
+
211
+ .footer-nav a:focus-visible {
212
+ outline: 2px solid color-mix(in srgb, var(--link) 50%, white 20%);
213
+ outline-offset: 3px;
214
+ }
215
+
216
+ footer small {
217
+ display: block;
218
+ color: var(--muted);
219
+ font-size: 0.875rem;
220
+ line-height: 1.6;
221
+ }
222
+
223
+ footer small a {
224
+ color: var(--link);
225
+ text-decoration: none;
226
+ font-weight: 600;
227
+ transition: color 180ms ease;
228
+ }
229
+
230
+ footer small a:hover,
231
+ footer small a:focus-visible {
232
+ color: var(--link-hover);
233
+ }
234
+
235
+ footer small a:focus-visible {
236
+ outline: 2px solid color-mix(in srgb, var(--link) 50%, white 20%);
237
+ outline-offset: 3px;
238
+ border-radius: 0.25rem;
239
+ }
240
+ `;
@@ -0,0 +1,9 @@
1
+ html {
2
+ font-size: 16px;
3
+ -webkit-text-size-adjust: 100%;
4
+ }
5
+
6
+ body {
7
+ margin: 0;
8
+ height: 100%;
9
+ }
@@ -0,0 +1,39 @@
1
+ <?layout href="_layout.html">
2
+
3
+ <my-component>
4
+ <script expr>
5
+ // These <script expr> tags are evaluated in-place as expressions and replaced by the result.
6
+ html`<p>The ${stack} stack is dead simple and feels like there's no framework at all.</p>`;
7
+ </script>
8
+ </my-component>
9
+
10
+ <script>
11
+ // This is ordinary client-only code.
12
+ console.log('Hello from the browser!');
13
+ </script>
14
+
15
+ <script type="module">
16
+ // This is also client-only code.
17
+ // Client-side modules are processed to vendorize NPM dependencies.
18
+ // There is no bundle step - modules are loaded via import maps.
19
+ console.log('Hello from a module!');
20
+ </script>
21
+
22
+ <script isomorphic>
23
+ // This code runs on both the client and server.
24
+ // Isomorphic scripts are always modules.
25
+ // Since it runs on the client, it will also vendorize NPM dependencies.
26
+ import { MyComponent } from './components/my-component';
27
+
28
+ // Thunderous components render declarative shadow DOM when defined on the server.
29
+ MyComponent.define('my-component');
30
+ </script>
31
+
32
+ <script server>
33
+ // This code runs on the server only.
34
+ // Server scripts are always modules.
35
+ // Exported values are available in <script expr> tags.
36
+ export default {
37
+ stack: 'Thunderous',
38
+ };
39
+ </script>
@@ -0,0 +1,5 @@
1
+ export default {
2
+ name: '{{ APP_NAME }}',
3
+ baseDir: 'src',
4
+ outDir: 'dist',
5
+ };
@@ -0,0 +1,49 @@
1
+ {
2
+ // Visit https://aka.ms/tsconfig to read more about this file
3
+ "compilerOptions": {
4
+ // File Layout
5
+ "rootDir": "./",
6
+ "outDir": "./dist",
7
+
8
+ // Environment Settings
9
+ // See also https://aka.ms/tsconfig/module
10
+ "module": "esnext",
11
+ "target": "esnext",
12
+
13
+ // For nodejs:
14
+ "lib": ["esnext"],
15
+ "types": ["node"],
16
+ "typeRoots": ["./node_modules/@types", "./types"],
17
+ "moduleResolution": "bundler",
18
+ "allowSyntheticDefaultImports": true,
19
+
20
+ // Other Outputs
21
+ "sourceMap": true,
22
+ "noEmit": true,
23
+ "declaration": false,
24
+ "declarationMap": false,
25
+
26
+ // Stricter Typechecking Options
27
+ "noUncheckedIndexedAccess": true,
28
+ "exactOptionalPropertyTypes": true,
29
+
30
+ // Style Options
31
+ "noImplicitAny": true,
32
+ "noImplicitReturns": true,
33
+ "noImplicitOverride": true,
34
+ "noUnusedLocals": true,
35
+ "noUnusedParameters": true,
36
+ "noFallthroughCasesInSwitch": true,
37
+ "noPropertyAccessFromIndexSignature": true,
38
+
39
+ // Recommended Options
40
+ "strict": true,
41
+ "verbatimModuleSyntax": true,
42
+ "isolatedModules": true,
43
+ "noUncheckedSideEffectImports": true,
44
+ "moduleDetection": "force",
45
+ "skipLibCheck": true
46
+ },
47
+ "include": ["./**/*.ts"],
48
+ "exclude": ["./**/*.tmp.ts"]
49
+ }
@@ -0,0 +1 @@
1
+ import '@total-typescript/ts-reset';