Theme workbench14 examples
Styling / Development
Theme workbench
Apply one token set to payment, signup, and launcher UI.
Previewlight site theme
Loading payment…
CodeApp.tsx
import {
LockerverseDonateLauncher,
LockerversePayment,
type LockerverseStyle,
type LockerverseTheme,
useLockerverseWidget,
} from "@lockerverse/react";
import "@lockerverse/react/styles.css";
import {
LockerverseSignup,
useLockerverseSignup,
} from "@lockerverse/react/signup";
import "@lockerverse/react/signup.css";
import { useState } from "react";
import { exampleRuntime } from "../example-runtime";
type ComponentName = "launcher" | "payment" | "signup";
type SurfaceName = "ink" | "paper" | "site";
const surfaceStyles = {
ink: {
"--lockerverse-bg": "#12100d",
"--lockerverse-border": "#433c34",
"--lockerverse-control": "#1c1915",
"--lockerverse-muted": "#afa79e",
"--lockerverse-placeholder": "#7c746b",
"--lockerverse-subtle": "#29251f",
"--lockerverse-text": "#fff9ef",
},
paper: {
"--lockerverse-bg": "#fffaf0",
"--lockerverse-border": "#d9cbb7",
"--lockerverse-control": "#f5eddf",
"--lockerverse-muted": "#756a5e",
"--lockerverse-placeholder": "#9c9083",
"--lockerverse-subtle": "#eee3d3",
"--lockerverse-text": "#211c17",
},
site: {},
} satisfies Record<SurfaceName, LockerverseStyle>;
function PaymentPreview({
style,
theme,
}: {
style: LockerverseStyle;
theme: LockerverseTheme;
}) {
const widget = useLockerverseWidget({
apiBaseUrl: exampleRuntime.apiBaseUrl,
communitySlug: exampleRuntime.communitySlug,
environment: "development",
sentryDsn: null,
widgetSlug: exampleRuntime.widgetSlug,
});
return (
<LockerversePayment
selection={[{ productSlug: "adult", quantity: 1 }]}
style={style}
theme={theme}
widget={widget}
/>
);
}
function SignupPreview({
style,
theme,
}: {
style: LockerverseStyle;
theme: LockerverseTheme;
}) {
const widget = useLockerverseSignup({
apiBaseUrl: exampleRuntime.apiBaseUrl,
communitySlug: exampleRuntime.communitySlug,
environment: "development",
sentryDsn: null,
signupSlug: exampleRuntime.signupSlug,
});
return <LockerverseSignup style={style} theme={theme} widget={widget} />;
}
function LauncherPreview({
style,
theme,
}: {
style: LockerverseStyle;
theme: LockerverseTheme;
}) {
const widget = useLockerverseWidget({
apiBaseUrl: exampleRuntime.apiBaseUrl,
communitySlug: exampleRuntime.communitySlug,
environment: "development",
sentryDsn: null,
widgetSlug: exampleRuntime.widgetSlug,
});
return (
<LockerverseDonateLauncher
buttonLabel="Donate"
position="inline"
style={style}
theme={theme}
widget={widget}
/>
);
}
export default function ThemingExample({
theme = "light",
}: {
theme?: LockerverseTheme;
}) {
const [component, setComponent] = useState<ComponentName>("payment");
const [surface, setSurface] = useState<SurfaceName>("site");
const [accent, setAccent] = useState("#f1511b");
const [radius, setRadius] = useState(10);
const style = {
...surfaceStyles[surface],
"--lockerverse-accent": accent,
"--lockerverse-radius": `${radius}px`,
} satisfies LockerverseStyle;
return (
<div className="theme-workbench">
<div className="theme-workbench__controls">
<fieldset>
<legend>Component</legend>
<div className="theme-workbench__options">
{(["payment", "signup", "launcher"] as const).map((name) => (
<button
aria-pressed={component === name}
key={name}
onClick={() => setComponent(name)}
type="button"
>
{name}
</button>
))}
</div>
</fieldset>
<fieldset>
<legend>Surface</legend>
<div className="theme-workbench__options">
{(["site", "paper", "ink"] as const).map((name) => (
<button
aria-pressed={surface === name}
key={name}
onClick={() => setSurface(name)}
type="button"
>
{name}
</button>
))}
</div>
</fieldset>
<label className="theme-workbench__color">
<span>Accent</span>
<input
aria-label="Theme accent color"
onChange={(event) => setAccent(event.currentTarget.value)}
type="color"
value={accent}
/>
<code>{accent}</code>
</label>
<label className="theme-workbench__radius">
<span>Radius</span>
<input
max="24"
min="0"
onChange={(event) => setRadius(event.currentTarget.valueAsNumber)}
type="range"
value={radius}
/>
<code>{radius}px</code>
</label>
</div>
<div className="theme-workbench__preview" data-component={component}>
{component === "payment" ? (
<PaymentPreview style={style} theme={theme} />
) : null}
{component === "signup" ? (
<SignupPreview style={style} theme={theme} />
) : null}
{component === "launcher" ? (
<LauncherPreview style={style} theme={theme} />
) : null}
</div>
</div>
);
}