diff --git a/.ladle/components.tsx b/.ladle/components.tsx
new file mode 100644
index 0000000..f019c99
--- /dev/null
+++ b/.ladle/components.tsx
@@ -0,0 +1,29 @@
+import React, { useEffect } from "react";
+import "../styles/globals.css";
+import "./styles.css";
+
+import type { GlobalProvider } from "@ladle/react";
+import { ThemeProvider, useTheme } from "next-themes";
+
+export const Provider: GlobalProvider = ({ children, globalState }) => {
+ const { setTheme } = useTheme();
+
+ useEffect(() => {
+ setTheme(globalState.theme);
+ }, [globalState.theme, setTheme]);
+
+ return (
+
+
+ {children}
+
+
+ );
+};
diff --git a/.ladle/config.mjs b/.ladle/config.mjs
new file mode 100644
index 0000000..c9beb4f
--- /dev/null
+++ b/.ladle/config.mjs
@@ -0,0 +1,4 @@
+/** @type {import('@ladle/react').UserConfig} */
+export default {
+ stories: "**/*.stories.{js,jsx,ts,tsx,mdx}",
+};
diff --git a/.ladle/fonts/EuropaGroteskSH-Lig.otf b/.ladle/fonts/EuropaGroteskSH-Lig.otf
new file mode 100644
index 0000000..d777259
Binary files /dev/null and b/.ladle/fonts/EuropaGroteskSH-Lig.otf differ
diff --git a/.ladle/fonts/EuropaGroteskSH-Med.otf b/.ladle/fonts/EuropaGroteskSH-Med.otf
new file mode 100644
index 0000000..394dac1
Binary files /dev/null and b/.ladle/fonts/EuropaGroteskSH-Med.otf differ
diff --git a/.ladle/fonts/EuropaGroteskSH-Reg.otf b/.ladle/fonts/EuropaGroteskSH-Reg.otf
new file mode 100644
index 0000000..d4a8d02
Binary files /dev/null and b/.ladle/fonts/EuropaGroteskSH-Reg.otf differ
diff --git a/.ladle/styles.css b/.ladle/styles.css
new file mode 100644
index 0000000..c0fbdad
--- /dev/null
+++ b/.ladle/styles.css
@@ -0,0 +1,41 @@
+@font-face {
+ /* includes font-weights from 600-900 */
+ font-family: "Europa";
+ src: url("/fonts/EuropaGroteskSH-Med.otf");
+ font-weight: 600 900;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ /* includes font-weights from 400-500 */
+ font-family: "Europa";
+ src: url("/fonts/EuropaGroteskSH-Reg.otf");
+ font-weight: 400 500;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ /* includes font-weights from 100-300 */
+ font-family: "Europa";
+ src: url("/fonts/EuropaGroteskSH-Lig.otf");
+ font-weight: 100 300;
+ font-style: normal;
+ font-display: swap;
+}
+
+:root {
+ /* define custom css var for the Europa font */
+ --font-europa-sans: "Europa", sans-serif;
+}
+
+* {
+ /* apply letter-spacing to all html elements */
+ @apply tracking-wider;
+}
+
+.font-mono {
+ /* reset the `tracking-wider` on the monospace font */
+ letter-spacing: initial;
+}
diff --git a/.npmignore b/.npmignore
index aadad2d..8c1c460 100644
--- a/.npmignore
+++ b/.npmignore
@@ -9,4 +9,8 @@ tsconfig.tsbuildinfo
*.spec.*
vitest.config.ts
setupTests.ts
-coverage
\ No newline at end of file
+coverage
+.ladle
+tailwind.config.ts
+postcss.config.cjs
+stories
diff --git a/README.md b/README.md
index 2f59a1a..2cc51de 100644
--- a/README.md
+++ b/README.md
@@ -18,4 +18,4 @@ https://www.npmjs.com/package/@risc0/ui
| Statements | Branches | Functions | Lines |
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
-| ![Statements](https://img.shields.io/badge/statements-40.64%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-72.85%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-54.54%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-40.64%25-red.svg?style=flat) |
+| ![Statements](https://img.shields.io/badge/statements-39.66%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-66.19%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-41.17%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-39.66%25-red.svg?style=flat) |
diff --git a/libs/core.ts b/libs/core.ts
deleted file mode 100644
index efd3683..0000000
--- a/libs/core.ts
+++ /dev/null
@@ -1,324 +0,0 @@
-/* c8 ignore start */
-/**
- * This is the core package of t3-env.
- * It contains the `createEnv` function that you can use to create your schema.
- * @module
- */
-
-import type { InferOutput, ObjectSchema } from "valibot";
-import { flatten as vFlatten, object as vObject, safeParse as vSafeParse } from "valibot";
-import type { AnySchema } from "./utils";
-
-/** @internal */
-export type ErrorMessage = T;
-
-/** @internal */
-export type Simplify = {
- [P in keyof T]: T[P];
-} & {};
-
-type Impossible> = Partial>;
-
-type UnReadonlyObject = T extends Readonly ? U : T;
-
-type Reduce[], TAcc> = TArr extends []
- ? TAcc
- : TArr extends [infer Head, ...infer Tail]
- ? Tail extends Record[]
- ? Head & Reduce
- : never
- : never;
-
-/**
- * The options that can be passed to the `createEnv` function.
- */
-export interface BaseOptions, TExtends extends Record[]> {
- /**
- * How to determine whether the app is running on the server or the client.
- * @default typeof window === "undefined"
- */
- isServer?: boolean;
-
- /**
- * Shared variables, often those that are provided by build tools and is available to both client and server,
- * but isn't prefixed and doesn't require to be manually supplied. For example `NODE_ENV`, `VERCEL_URL` etc.
- */
- shared?: TShared;
-
- /**
- * Extend presets
- */
- extends?: TExtends;
-
- /**
- * Called when validation fails. By default the error is logged,
- * and an error is thrown telling what environment variables are invalid.
- */
- onValidationError?: (error) => never;
-
- /**
- * Called when a server-side environment variable is accessed on the client.
- * By default an error is thrown.
- */
- onInvalidAccess?: (variable: string) => never;
-
- /**
- * Whether to skip validation of environment variables.
- * @default false
- */
- skipValidation?: boolean;
-
- /**
- * By default, this library will feed the environment variables directly to
- * the Valibot validator.
- *
- * This means that if you have an empty string for a value that is supposed
- * to be a number (e.g. `PORT=` in a ".env" file), Valibot will incorrectly flag
- * it as a type mismatch violation. Additionally, if you have an empty string
- * for a value that is supposed to be a string with a default value (e.g.
- * `DOMAIN=` in an ".env" file), the default value will never be applied.
- *
- * In order to solve these issues, we recommend that all new projects
- * explicitly specify this option as true.
- */
- emptyStringAsUndefined?: boolean;
-}
-
-/**
- * Using this interface doesn't validate all environment variables are specified
- * in the `runtimeEnv` object. You may want to use `StrictOptions` instead if
- * your framework performs static analysis and tree-shakes unused variables.
- */
-export interface LooseOptions, TExtends extends Record[]>
- extends BaseOptions {
- runtimeEnvStrict?: never;
-
- /**
- * What object holds the environment variables at runtime. This is usually
- * `process.env` or `import.meta.env`.
- */
- // Unlike `runtimeEnvStrict`, this doesn't enforce that all environment variables are set.
- runtimeEnv: Record;
-}
-
-/**
- * Using this interface validates all environment variables are specified
- * in the `runtimeEnv` object. If you miss one, you'll get a type error. Useful
- * if you want to make sure all environment variables are set for frameworks that
- * perform static analysis and tree-shakes unused variables.
- */
-export interface StrictOptions<
- TPrefix extends string | undefined,
- TServer extends Record,
- TClient extends Record,
- TShared extends Record,
- TExtends extends Record[],
-> extends BaseOptions {
- /**
- * Runtime Environment variables to use for validation - `process.env`, `import.meta.env` or similar.
- * Enforces all environment variables to be set. Required in for example Next.js Edge and Client runtimes.
- */
- runtimeEnvStrict: Record<
- | {
- [TKey in keyof TClient]: TPrefix extends undefined ? never : TKey extends `${TPrefix}${string}` ? TKey : never;
- }[keyof TClient]
- | {
- [TKey in keyof TServer]: TPrefix extends undefined ? TKey : TKey extends `${TPrefix}${string}` ? never : TKey;
- }[keyof TServer]
- | {
- [TKey in keyof TShared]: TKey extends string ? TKey : never;
- }[keyof TShared],
- string | boolean | number | undefined
- >;
- runtimeEnv?: never;
-}
-
-/**
- * This interface is used to define the client-side environment variables.
- * It's used in conjunction with the `clientPrefix` option to ensure
- * that all client-side variables are prefixed with the same string.
- * Common examples of prefixes are `NEXT_PUBLIC_`, `NUXT_PUBLIC` or `PUBLIC_`.
- */
-export interface ClientOptions> {
- /**
- * The prefix that client-side variables must have. This is enforced both at
- * a type-level and at runtime.
- */
- clientPrefix: TPrefix;
-
- /**
- * Specify your client-side environment variables schema here. This way you can ensure the app isn't
- * built with invalid env vars.
- */
- client: Partial<{
- [TKey in keyof TClient]: TKey extends `${TPrefix}${string}`
- ? TClient[TKey]
- : ErrorMessage<`${TKey extends string ? TKey : never} is not prefixed with ${TPrefix}.`>;
- }>;
-}
-
-/**
- * This interface is used to define the schema for your
- * server-side environment variables.
- */
-export interface ServerOptions> {
- /**
- * Specify your server-side environment variables schema here. This way you can ensure the app isn't
- * built with invalid env vars.
- */
- server: Partial<{
- [TKey in keyof TServer]: TPrefix extends undefined
- ? TServer[TKey]
- : TPrefix extends ""
- ? TServer[TKey]
- : TKey extends `${TPrefix}${string}`
- ? ErrorMessage<`${TKey extends `${TPrefix}${string}` ? TKey : never} should not prefixed with ${TPrefix}.`>
- : TServer[TKey];
- }>;
-}
-
-export type ServerClientOptions<
- TPrefix extends string | undefined,
- TServer extends Record,
- TClient extends Record,
-> =
- | (ClientOptions & ServerOptions)
- | (ServerOptions & Impossible>)
- | (ClientOptions & Impossible>);
-
-export type EnvOptions<
- TPrefix extends string | undefined,
- TServer extends Record,
- TClient extends Record,
- TShared extends Record,
- TExtends extends Record[],
-> =
- | (LooseOptions & ServerClientOptions)
- | (StrictOptions & ServerClientOptions);
-
-type TPrefixFormat = string | undefined;
-type TServerFormat = Record;
-type TClientFormat = Record;
-type TSharedFormat = Record;
-type TExtendsFormat = Record[];
-
-/**
- * Creates a new environment variable schema.
- */
-export type CreateEnv<
- TServer extends TServerFormat,
- TClient extends TClientFormat,
- TShared extends TSharedFormat,
- TExtends extends TExtendsFormat,
-> = Readonly<
- Simplify<
- InferOutput> &
- InferOutput> &
- InferOutput> &
- UnReadonlyObject>
- >
->;
-
-/**
- * Create a new environment variable schema.
- */
-export function createEnv<
- TPrefix extends TPrefixFormat,
- TServer extends TServerFormat = NonNullable,
- TClient extends TClientFormat = NonNullable,
- TShared extends TSharedFormat = NonNullable,
- const TExtends extends TExtendsFormat = [],
->(opts: EnvOptions): CreateEnv {
- const runtimeEnv = opts.runtimeEnvStrict ?? opts.runtimeEnv ?? process.env;
-
- const emptyStringAsUndefined = opts.emptyStringAsUndefined ?? false;
- if (emptyStringAsUndefined) {
- for (const [key, value] of Object.entries(runtimeEnv)) {
- if (value === "") {
- delete runtimeEnv[key];
- }
- }
- }
-
- const skip = !!opts.skipValidation;
-
- if (skip) {
- return runtimeEnv as any;
- }
-
- const _client = typeof opts.client === "object" ? opts.client : {};
- const _server = typeof opts.server === "object" ? opts.server : {};
- const _shared = typeof opts.shared === "object" ? opts.shared : {};
- const client = vObject(_client);
- const server = vObject(_server);
- const shared = vObject(_shared);
- const isServer = opts.isServer ?? (typeof window === "undefined" || "Deno" in window);
-
- const allClient = vObject({ ...client.entries, ...shared.entries });
- const allServer = vObject({ ...server.entries, ...shared.entries, ...client.entries });
- const parsed = isServer
- ? vSafeParse(allServer, runtimeEnv) // on server we can validate all env vars
- : vSafeParse(allClient, runtimeEnv); // on client we can only validate the ones that are exposed
-
- const onValidationError =
- opts.onValidationError ??
- ((error) => {
- console.error("❌ Invalid environment variables:", vFlatten(error).nested);
- throw new Error("Invalid environment variables");
- });
-
- const onInvalidAccess =
- opts.onInvalidAccess ??
- ((_variable: string) => {
- throw new Error("❌ Attempted to access a server-side environment variable on the client");
- });
-
- if (parsed.success === false) {
- return onValidationError(parsed.issues);
- }
-
- const isServerAccess = (prop: string) => {
- if (!opts.clientPrefix) {
- return true;
- }
- return !prop.startsWith(opts.clientPrefix) && !(prop in shared.entries);
- };
- const isValidServerAccess = (prop: string) => {
- return isServer || !isServerAccess(prop);
- };
- const ignoreProp = (prop: string) => {
- return prop === "__esModule" || prop === "$$typeof";
- };
-
- const extendedObj = (opts.extends ?? []).reduce((acc, curr) => {
- return Object.assign(acc, curr);
- }, {});
- const fullObj = Object.assign(parsed.output, extendedObj);
-
- const env = new Proxy(fullObj, {
- get(target, prop) {
- if (typeof prop !== "string") {
- return undefined;
- }
- if (ignoreProp(prop)) {
- return undefined;
- }
- if (!isValidServerAccess(prop)) {
- return onInvalidAccess(prop);
- }
- return Reflect.get(target, prop);
- },
- // Maybe reconsider this in the future:
- // https://github.com/t3-oss/t3-env/pull/111#issuecomment-1682931526
- // set(_target, prop) {
- // // Readonly - this is the error message you get from assigning to a frozen object
- // throw new Error(
- // typeof prop === "string"
- // ? `Cannot assign to read only property ${prop} of object #