Skip to content

Commit a8fbaa4

Browse files
author
DevBot
committed
fix(create): style the starter contact form and 404 page to match the shell
Visual review of the packed starter found the two request-time pages shipping raw UA-default form controls and link colors against the styled shell. Both pages now carry a scoped StyleSheet on the design tokens. contact.tsx keeps its logic in definePage render: the inner styled element must NOT reuse the route tag — a module that self-registers its route tag via defineElement makes the entry skip the page class (entry-orchestrator #952 rule), which silently bypasses the definePage render along with its request/actionData context.
1 parent efb12e1 commit a8fbaa4

2 files changed

Lines changed: 85 additions & 25 deletions

File tree

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,43 @@
11
/** @jsxImportSource @openelement/element */
2-
import { definePage } from '@openelement/app';
2+
import { defineElement, definePage } from '@openelement/app';
3+
import { StyleSheet } from '@openelement/element';
34

4-
/**
5-
* Styled 404 (#923): the request-time server renders this page with a 404
6-
* status for unmatched paths; SSG builds also emit it as static 404.html.
7-
*/
8-
const NotFoundPage = definePage({
9-
renderIntent: { mode: 'dynamic' },
10-
head: { title: '404 — openElement' },
5+
export const tagName = 'not-found-page';
6+
7+
const styles = new StyleSheet();
8+
styles.replaceSync(`
9+
:host { display: block; }
10+
h1 { font-family: var(--font-serif); font-size: 2.4rem; letter-spacing: -0.015em; margin: 0.75rem 0 0.5rem; font-weight: 700; }
11+
p { color: var(--ink-2); line-height: 1.6; }
12+
a { color: var(--brand); font-weight: 600; text-decoration: none; }
13+
a:hover { text-decoration: underline; }
14+
`);
15+
16+
defineElement(tagName, {
17+
styles,
1118
render() {
1219
return (
13-
<main>
20+
<>
1421
<h1>404</h1>
1522
<p>The page you are looking for does not exist.</p>
1623
<p>
1724
<a href='/'>Back to the homepage</a>
1825
</p>
19-
</main>
26+
</>
2027
);
2128
},
2229
});
2330

31+
/**
32+
* Styled 404 (#923): the request-time server renders this page with a 404
33+
* status for unmatched paths; SSG builds also emit it as static 404.html.
34+
*/
35+
const NotFoundPage = definePage({
36+
renderIntent: { mode: 'dynamic' },
37+
head: { title: '404 — openElement' },
38+
render() {
39+
return <not-found-page />;
40+
},
41+
});
42+
2443
export default NotFoundPage;

packages/create/templates/app/routes/contact.tsx

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,68 @@
11
/** @jsxImportSource @openelement/element */
22
import {
3+
defineElement,
34
definePage,
45
fail,
56
type OpenElementActionFailure,
67
redirect,
78
useActionData,
89
} from '@openelement/app';
10+
import { StyleSheet } from '@openelement/element';
911

1012
interface ContactActionData {
1113
error?: string;
1214
email?: string;
1315
}
1416

17+
const styles = new StyleSheet();
18+
styles.replaceSync(`
19+
:host { display: block; }
20+
h1 { font-family: var(--font-serif); font-size: 2.4rem; letter-spacing: -0.015em; margin: 0.75rem 0 0.5rem; font-weight: 700; }
21+
.sub { color: var(--ink-2); line-height: 1.6; max-width: 52ch; margin: 0 0 1.75rem; }
22+
form { display: flex; gap: 0.6rem; flex-wrap: wrap; }
23+
input {
24+
font: inherit; min-width: 16rem; padding: 0.55rem 0.8rem; color: var(--ink);
25+
border: 1px solid var(--line); border-radius: 6px; background: #fff;
26+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
27+
}
28+
input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px #8262db2e; }
29+
button {
30+
font: inherit; font-weight: 600; padding: 0.55rem 1.1rem; cursor: pointer;
31+
border: 1px solid var(--brand); border-radius: 6px; background: var(--brand); color: #fff;
32+
transition: opacity 0.15s ease;
33+
}
34+
button:hover { opacity: 0.88; }
35+
#error { color: #c92a2a; margin: 1rem 0 0; }
36+
#thanks { color: var(--brand); font-weight: 600; margin: 1rem 0 0; }
37+
`);
38+
39+
defineElement('contact-form-view', {
40+
styles,
41+
render(props: { email?: string; error?: string; subscribed?: string }) {
42+
return (
43+
<>
44+
<h1>Stay in the loop</h1>
45+
<p class='sub'>
46+
A request-time route: the plain form works without JavaScript, and morphs in place with
47+
it.
48+
</p>
49+
<form method='post' data-open-enhance>
50+
<input
51+
id='email'
52+
name='email'
53+
type='text'
54+
value={props.email ?? ''}
55+
placeholder='you@example.com'
56+
/>
57+
<button type='submit'>Subscribe</button>
58+
</form>
59+
{props.error ? <p id='error'>{props.error}</p> : null}
60+
{props.subscribed ? <p id='thanks'>subscribed={props.subscribed}</p> : null}
61+
</>
62+
);
63+
},
64+
});
65+
1566
/**
1667
* A request-time route exercising the 0.42 WC Application Loop: plain HTML
1768
* form works without JavaScript (422 echo / 303 PRG), data-open-enhance
@@ -32,21 +83,11 @@ const ContactPage = definePage({
3283
const actionData = useActionData() as ContactActionData | undefined;
3384
const subscribed = request ? new URL(request.url).searchParams.get('subscribed') : undefined;
3485
return (
35-
<main>
36-
<h1>Stay in the loop</h1>
37-
<form method='post' data-open-enhance>
38-
<input
39-
id='email'
40-
name='email'
41-
type='text'
42-
value={actionData?.email ?? ''}
43-
placeholder='you@example.com'
44-
/>
45-
<button type='submit'>Subscribe</button>
46-
</form>
47-
{actionData?.error ? <p id='error'>{actionData.error}</p> : null}
48-
{subscribed ? <p id='thanks'>subscribed={subscribed}</p> : null}
49-
</main>
86+
<contact-form-view
87+
email={actionData?.email}
88+
error={actionData?.error}
89+
subscribed={subscribed ?? undefined}
90+
/>
5091
);
5192
},
5293
});

0 commit comments

Comments
 (0)