Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/animated-letters.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function* Letter({letter, index}) {
});
});

yield <span style={{...style, color: "black"}}>{letter}</span>;
yield <span style={style}>{letter}</span>;
}
}

Expand Down
4 changes: 1 addition & 3 deletions examples/hackernews.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,7 @@ function Pager({page}) {
async function List({page, start = (page - 1) * 30 + 1}) {
const result = await fetch(`https://api.hnpwa.com/v0/news/${page}.json`);
const stories = await result.json();
const items = stories.map((story) => (
<Story story={story} key={story.id} />
));
const items = stories.map((story) => <Story story={story} key={story.id} />);
return (
<Fragment>
<Pager page={page} />
Expand Down
5 changes: 2 additions & 3 deletions examples/mathml.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,14 @@ const style = `
line-height: 1.6;
}
.math-example {
border: 1px solid #ddd;
border: 1px solid var(--text-color, #ddd);
padding: 20px;
margin: 20px 0;
background: #f9f9f9;
background: rgba(128, 128, 128, 0.1);
border-radius: 8px;
}
.math-example h3 {
margin-top: 0;
color: #333;
}
math {
font-size: 1.2em;
Expand Down
86 changes: 46 additions & 40 deletions examples/password-strength.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,55 @@ import {renderer} from "@b9g/crank/dom";
// Adapted from https://backbonenotbad.hyperclay.com/
// https://gist.github.com/panphora/8f4d620ae92e8b28dcb4f20152185749
function* PasswordStrength() {
const requirements = [
{label: '8+ characters', check: (pwd) => pwd.length >= 8},
{label: '12+ characters', check: (pwd) => pwd.length >= 12},
{label: 'Lowercase letter', check: (pwd) => /[a-z]/.test(pwd)},
{label: 'Uppercase letter', check: (pwd) => /[A-Z]/.test(pwd)},
{label: 'Number', check: (pwd) => /\d/.test(pwd)},
{label: 'Special character', check: (pwd) => /[^a-zA-Z0-9]/.test(pwd)},
];
const requirements = [
{label: "8+ characters", check: (pwd) => pwd.length >= 8},
{label: "12+ characters", check: (pwd) => pwd.length >= 12},
{label: "Lowercase letter", check: (pwd) => /[a-z]/.test(pwd)},
{label: "Uppercase letter", check: (pwd) => /[A-Z]/.test(pwd)},
{label: "Number", check: (pwd) => /\d/.test(pwd)},
{label: "Special character", check: (pwd) => /[^a-zA-Z0-9]/.test(pwd)},
];

let password = '';
let password = "";

for ({} of this) {
yield (
<div class="w-80 p-6 bg-white rounded-xl shadow-lg space-y-4">
<input
type="password"
value={password}
oninput={(e) => this.refresh(() => password = e.target.value)}
placeholder="Enter password"
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2"
/>
<div class="space-y-2">
{requirements.map((req, idx) => {
const isMet = req.check(password);
return (
<div key={idx} class="flex items-center gap-2">
<div class={`w-5 h-5 rounded-full flex items-center justify-center text-xs font-bold ${isMet ? 'bg-green-500 text-white' : 'bg-gray-200 text-gray-400'}`}>
{isMet ? '✓' : ''}
</div>
<span class={`text-sm ${isMet ? 'text-green-600 font-medium' : 'text-gray-500'}`}>
{req.label}
</span>
</div>
);
})}
</div>
</div>
);
}
for ({} of this) {
yield (
<div class="w-80 p-6 bg-white rounded-xl shadow-lg space-y-4">
<input
type="password"
value={password}
oninput={(e) => this.refresh(() => (password = e.target.value))}
placeholder="Enter password"
class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2"
/>
<div class="space-y-2">
{requirements.map((req, idx) => {
const isMet = req.check(password);
return (
<div key={idx} class="flex items-center gap-2">
<div
class={`w-5 h-5 rounded-full flex items-center justify-center text-xs font-bold ${isMet ? "bg-green-500 text-white" : "bg-gray-200 text-gray-400"}`}
>
{isMet ? "✓" : ""}
</div>
<span
class={`text-sm ${isMet ? "text-green-600 font-medium" : "text-gray-500"}`}
>
{req.label}
</span>
</div>
);
})}
</div>
</div>
);
}
}

const script = document.createElement('script');
script.src = 'https://cdn.tailwindcss.com';
const script = document.createElement("script");
script.src = "https://cdn.tailwindcss.com";
document.head.appendChild(script);
await new Promise((resolve) => script.addEventListener("load", () => resolve(), {once: true}));
await new Promise((resolve) =>
script.addEventListener("load", () => resolve(), {once: true}),
);
renderer.render(<PasswordStrength />, document.body);
4 changes: 3 additions & 1 deletion examples/todomvc.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ link.href = "https://unpkg.com/[email protected]/index.css";
// remove default stylesheet for playground
document.head.querySelector("link").remove();
document.head.appendChild(link);
await new Promise((resolve) => link.addEventListener("load", () => resolve(), {once: true}));
await new Promise((resolve) =>
link.addEventListener("load", () => resolve(), {once: true}),
);

renderer.render(<App />, document.body);
60 changes: 45 additions & 15 deletions website/src/components/code-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {css} from "@emotion/css";
import {debounce} from "../utils/fns.js";
import {transform} from "../plugins/babel.js";
import {extractData} from "./serialize-javascript.js";
import {getColorSchemeScript} from "../utils/color-scheme.js";

function generateJavaScriptIFrameHTML(
id: number,
Expand All @@ -12,29 +13,55 @@ function generateJavaScriptIFrameHTML(
): string {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<script>
${getColorSchemeScript()}
</script>
<style>
/* Ensure colors cascade to all elements */
html, body {
background-color: #0a0e1f;
color: #f5f9ff;
margin: 0;
padding: 0;
}

/* Light mode overrides */
html.color-scheme-light, html.color-scheme-light body {
background-color: #e7f4f5;
color: #0a0e1f;
}

* {
box-sizing: border-box;
}

/* CSS variables for compatibility with external CSS */
:root {
--bg-color: #0a0e1f;
--text-color: #f5f9ff;
--highlight-color: #daa520;
}
.color-scheme-light {
--bg-color: #e7f4f5;
--text-color: #0a0e1f;
--highlight-color: #daa520;
}

body {
font-family: sans-serif;
}
</style>
<link
rel="stylesheet"
type="text/css"
href=${staticURLs!["client.css"]}
/>
</head>
<body>
<!-- TODO: extract these scripts to a separate file or something -->
<script>
const colorScheme = sessionStorage.getItem("color-scheme") ||
(
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light"
);
if (colorScheme === "dark") {
document.body.classList.remove("color-scheme-light");
} else {
document.body.classList.add("color-scheme-light");
}
</script>
<script>
window.addEventListener("load", (ev) => {
window.parent.postMessage(
Expand Down Expand Up @@ -86,6 +113,7 @@ function generateJavaScriptIFrameHTML(
</script>
<script type="module">${code}</script>
</body>
</html>
`;
}

Expand Down Expand Up @@ -119,8 +147,10 @@ function generatePythonIFrameHTML(
: "light"
);
if (colorScheme === "dark") {
document.documentElement.classList.remove("color-scheme-light");
document.body.classList.remove("color-scheme-light");
} else {
document.documentElement.classList.add("color-scheme-light");
document.body.classList.add("color-scheme-light");
}
</script>
Expand Down
44 changes: 13 additions & 31 deletions website/src/components/color-scheme-toggle.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import {jsx} from "@b9g/crank/standalone";
import type {Context} from "@b9g/crank/standalone";
import {
getColorScheme,
setColorScheme,
applyColorScheme,
syncIframes,
type ColorScheme,
} from "../utils/color-scheme.js";

// the website defaults to dark mode
let colorScheme: string | undefined;
let colorScheme: ColorScheme | undefined;
if (typeof window !== "undefined") {
colorScheme =
sessionStorage.getItem("color-scheme") ||
(window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light");
if (colorScheme === "dark") {
document.body.classList.remove("color-scheme-light");
} else {
document.body.classList.add("color-scheme-light");
}
colorScheme = getColorScheme();
applyColorScheme(colorScheme);
}

// This component would not work with multiple instances, insofar as clicking
Expand All @@ -22,30 +21,13 @@ if (typeof window !== "undefined") {
export function ColorSchemeToggle(this: Context) {
const onclick = () => {
colorScheme = colorScheme === "dark" ? "light" : "dark";
sessionStorage.setItem("color-scheme", colorScheme);
setColorScheme(colorScheme);
this.refresh();
};

if (typeof window !== "undefined") {
if (colorScheme === "dark") {
document.body.classList.remove("color-scheme-light");
for (const iframe of Array.from(
document.querySelectorAll(".playground-iframe"),
)) {
(
iframe as HTMLIFrameElement
).contentWindow?.document.body.classList.remove("color-scheme-light");
}
} else {
document.body.classList.add("color-scheme-light");
for (const iframe of Array.from(
document.querySelectorAll(".playground-iframe"),
)) {
(
iframe as HTMLIFrameElement
).contentWindow?.document.body.classList.add("color-scheme-light");
}
}
applyColorScheme(colorScheme!);
syncIframes(colorScheme!);
}

return jsx`
Expand Down
14 changes: 2 additions & 12 deletions website/src/components/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,12 @@ import {extractCritical} from "@emotion/server";
import {Page, Link, Script, Storage} from "./esbuild.js";
import {Navbar} from "./navbar.js";
import {StaticURLsJSON} from "./static-urls-json.js";
import {getColorSchemeScript} from "../utils/color-scheme.js";

function ColorSchemeScript() {
// This script must be executed as early as possible to prevent a FOUC.
// It also cannot be `type="module"` because that will also cause an FOUC.
const scriptText = `
(() => {
const colorScheme = sessionStorage.getItem("color-scheme") ||
(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark" : "light"
);
if (colorScheme === "dark") {
document.body.classList.remove("color-scheme-light");
} else {
document.body.classList.add("color-scheme-light");
}
})()`;
const scriptText = `(() => { ${getColorSchemeScript()} })()`;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getColorSchemeScript() utility could be a component instead.

return jsx`
<script>
<${Raw} value=${scriptText} />
Expand Down
Loading