Windows and the renderer
Build the notes interface
Create the semantic HTML shell for a note list, editor form, and status message in the renderer.
The renderer is a web page. So we start the interface the same way we’d start any web page: with plain, accessible HTML. Replace src/index.html with this two-column layout:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self'"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Desktop Notes</title>
</head>
<body>
<main class="app-shell">
<aside>
<button id="new-note" type="button">New note</button>
<ul id="notes"></ul>
</aside>
<form id="editor">
<input id="title" aria-label="Title" required />
<textarea id="body" aria-label="Note" rows="16"></textarea>
<button type="submit">Save note</button>
</form>
</main>
<p id="status" role="status"></p>
</body>
</html>
On the left, a button to create a note and the list of notes. On the right, a form with a title, a body, and a save button. Below, a status line where we’ll report saves and errors.
The Webpack template injects the renderer bundle into this page for you. If your version of the template ships an explicit <script> tag instead, keep that tag. Don’t add a second one.
Accessibility for free
A <form> with a submit button works with the keyboard out of the box. Press Enter in the title field and the form submits. The role="status" on the paragraph means screen readers announce whatever we write there, so “Saved 3 notes” gets read aloud without extra code.
The aria-label attributes give the fields a name for assistive technology. If the design later hides the purpose of a field, add a visible label. aria-label is not a substitute for text people can see.
The content security policy
The Content-Security-Policy meta tag tells Chromium where scripts and styles may come from. Here the answer is 'self' only: the app’s own bundled files.
That means no inline <script> tags, no onclick="..." attributes, and no stylesheet loaded from a CDN. All renderer code lives in the bundled entry.
At some point you’ll add an inline handler, see a CSP error in DevTools, and be tempted to add 'unsafe-inline' to script-src. Don’t. Move the code into src/renderer.js instead. The policy is doing exactly what we want: refusing to run code that isn’t ours.
No desktop authority yet
Look at what this page can do right now. Buttons can change the DOM. The form can validate its fields. That’s all.
Nothing here can read a file or open a native dialog. The page has no more power than a browser tab. That is the boundary we want, and the next module opens exactly one door in it.
Before moving on, run the app and use only the keyboard to reach every control. Then submit the form with an empty title. The browser’s built-in required-field message should appear. We get that validation for free, so we won’t write custom code for it.
Lesson completed