@akanjs/cli 2.4.0-rc.0 → 2.4.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,14 @@ Use this for shared UI rules across module, scalar, app UI, and docs components.
15
15
  - Client components can use store hooks and event handlers where interaction is required.
16
16
  - Use generated model types from app client or module constants; do not invent duplicate UI-only model shapes.
17
17
 
18
+ ## Customizing Framework Components (Slot Overrides)
19
+ - When a default `akanjs/ui` component (Button, Select, Modal, Table, Input, Radio, DatePicker, Loading, …) is too restrictive for the design, re-skin it per route with a slot override instead of forking, wrapping every call site, or fighting it with `!important`.
20
+ - Write the drop-in replacement in `apps/<app>/ui/`, typed against the slot contract — `AkanModalComponent`, or `AkanUiOverrides["<Slot>"]` for any other slot — so it is compile-checked as a real substitute. Compose the framework's headless parts (e.g. `Dialog`) rather than re-implementing focus trapping, portals, or scroll-lock.
21
+ - Bind it in a logic-free `page/**/_overrides.tsx` manifest: imports plus a single `export default override({ Slot: BrandComponent })` (from `akanjs/ui`), no `"use client"`.
22
+ - Place the manifest at `page/` for an app-wide skin, or inside a route group/segment to scope it; nested manifests merge over ancestors slot-by-slot (closest ancestor wins, unlisted slots keep inheriting).
23
+ - Compound components expose one slot per leaf named `<Base><Sub>` (e.g. `InputPassword`, `RadioItem`, `LoadingSpin`); override only the leaves you need.
24
+ - See the `references/ui/customize` docs page for the full slot list and examples.
25
+
18
26
  ## Codegen Rules
19
27
  - Do not put business workflow decisions in render code.
20
28
  - Do not use undocumented UI components or props.
@@ -15,6 +15,24 @@ Use TailwindCSS and DaisyUI in a theme-safe, composable way for Akan UI and docs
15
15
  - Use consistent density and spacing within one component family.
16
16
  - Keep custom CSS files rare and scoped to cases utilities cannot express cleanly.
17
17
 
18
+ ## Theme Customization (`apps/<app>/page/styles.css`)
19
+ - The app theme is defined in `apps/<app>/page/styles.css` with daisyUI v5 `@plugin "daisyui/theme"` blocks (typically `light` and a `default: true` `dark`).
20
+ - Brand-level customization means tuning the whole theme block, not only the color tokens. A theme that only changes `--color-*` still looks like the default framework skin.
21
+ - Color tokens: `--color-primary`, `--color-secondary`, `--color-accent`, `--color-neutral`, `--color-info`, `--color-success`, `--color-warning`, `--color-error`, `--color-base-100/200/300`, and their `*-content` pairs.
22
+ - Shape and feel tokens (set these too):
23
+ - `--radius-selector` — rounding for checkbox, toggle, badge.
24
+ - `--radius-field` — rounding for button, input, select, tab.
25
+ - `--radius-box` — rounding for card, modal, alert.
26
+ - `--size-selector`, `--size-field` — base scale (density) of selector and field controls.
27
+ - `--border` — component border width (e.g. `1px`, `2px`).
28
+ - `--depth` — `0` flat, `1` adds a subtle 3D lift to components.
29
+ - `--noise` — `0` off, `1` adds a grain texture to surfaces.
30
+ - Keep shape/feel tokens consistent across the light and dark theme blocks unless the design deliberately differs by mode.
31
+
32
+ ## When Utilities Are Not Enough
33
+ - Prefer theme tokens and DaisyUI component classes over one-off utility stacks that re-implement a component's look.
34
+ - When a framework `akanjs/ui` component is structurally too restrictive, re-skin it with a `page/**/_overrides.tsx` slot override (see the componentRule guideline), not with `!important` utilities or a fork.
35
+
18
36
  ## Codegen Rules
19
37
  - Do not hardcode hex colors or one-off brand colors unless the existing file already defines that design system.
20
38
  - Do not use important flags to fight component composition.
@@ -22,6 +22,12 @@ Use this as the compact framework context for AI codegen. It should explain how
22
22
  - Keep business decisions in constant, document, or service; keep API exposure in signal; keep client coordination in store; keep rendering in UI files.
23
23
  - Use direct module imports where scanner rules expect them, and avoid inventing new top-level app folders.
24
24
 
25
+ ## Theming And UI Customization
26
+ When a request implies a distinct look and feel, do not stop at colors — customize both the theme and, when needed, the components.
27
+
28
+ - **Theme (`apps/<app>/page/styles.css`).** The app theme is one or more daisyUI v5 `@plugin "daisyui/theme"` blocks. Match the brand by tuning the whole block, not only the `--color-*` tokens: set corner rounding (`--radius-selector`, `--radius-field`, `--radius-box`), control density (`--size-selector`, `--size-field`), outline weight (`--border`), and surface treatment (`--depth`, `--noise`). New workspaces ship these knobs at neutral defaults so they are visible to tune. Fetch `get_guideline` with `cssRule` for the full variable reference before a deep theme pass.
29
+ - **Components (`page/**/_overrides.tsx`).** When a default `akanjs/ui` component (Button, Modal, Table, Input, Select, …) is too restrictive for the design, re-skin it per route instead of forking, wrapping, or fighting it with utility classes. Write a drop-in replacement in `apps/<app>/ui/` typed against the slot contract (`AkanModalComponent`, or `AkanUiOverrides["<Slot>"]`), composing the framework's headless parts, then bind it in a `page/**/_overrides.tsx` manifest with a single `export default override({ Slot: BrandComponent })`. Overrides cascade down the route tree like layouts (closest ancestor wins). Fetch `get_guideline` with `componentRule` and read the `references/ui/customize` docs page for the slot list and patterns.
30
+
25
31
  ## Review Checklist
26
32
  - The instruction points to current docs pages, not removed docs routes.
27
33
  - Generated examples use current Akan builder APIs and scanner-friendly filenames.
@@ -4287,9 +4287,10 @@ class IncrementalBuilderHost {
4287
4287
  // pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
4288
4288
  var backendMsgTypeSet = new Set(["build-route"]);
4289
4289
  var BACKEND_RESTART_DEBOUNCE_MS = 120;
4290
- var BACKEND_GRACEFUL_TIMEOUT_MS = 3000;
4290
+ var BACKEND_GRACEFUL_TIMEOUT_MS = 8000;
4291
4291
  var BACKEND_RECOVERY_BASE_DELAY_MS = 1000;
4292
4292
  var BACKEND_RECOVERY_MAX_DELAY_MS = 30000;
4293
+ var BACKEND_RECOVERY_MAX_ATTEMPTS = 5;
4293
4294
  var BACKEND_STDERR_TAIL_LIMIT = 40;
4294
4295
  var BUILDER_READY_TIMEOUT_MS = 150000;
4295
4296
  var BUILDER_START_MAX_ATTEMPTS = 3;
@@ -4314,6 +4315,8 @@ var shouldRestartBackendByDevPlan = (message) => {
4314
4315
  return message.devPlan.actions.includes("restart-backend");
4315
4316
  };
4316
4317
  var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
4318
+ var shouldAbandonBackendRecovery = (attempts, maxAttempts = BACKEND_RECOVERY_MAX_ATTEMPTS) => attempts >= maxAttempts;
4319
+ var normalizeBackendReportedGeneration = (generation) => generation >= 0 ? generation : undefined;
4317
4320
  var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
4318
4321
  var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
4319
4322
  var generationValue = (generation) => generation ?? -1;
@@ -4562,6 +4565,16 @@ class AkanAppHost {
4562
4565
  this.#replayBuilderState();
4563
4566
  return;
4564
4567
  }
4568
+ if (msg.type === "build-status") {
4569
+ const status = this.#recordBackendBuildStatus({
4570
+ generation: normalizeBackendReportedGeneration(msg.data.generation),
4571
+ ok: msg.data.ok,
4572
+ files: msg.data.files,
4573
+ message: msg.data.message
4574
+ });
4575
+ this.#sendOrQueueBuildStatus(status);
4576
+ return;
4577
+ }
4565
4578
  if (backendMsgTypeSet.has(msg.type))
4566
4579
  this.#sendToBuilder(msg);
4567
4580
  },
@@ -4723,6 +4736,19 @@ class AkanAppHost {
4723
4736
  #scheduleBackendRecovery(reason) {
4724
4737
  if (this.#backendRecoveryTimer || this.#backend)
4725
4738
  return;
4739
+ if (shouldAbandonBackendRecovery(this.#backendRecoveryAttempts)) {
4740
+ const message = `Backend exited ${this.#backendRecoveryAttempts} times in a row (${reason}); waiting for a server-side edit to retry.`;
4741
+ this.#setBackendLifecycleState("stopped", `gave up after ${this.#backendRecoveryAttempts} recovery attempts`);
4742
+ this.logger.error(`[backend-recovery] ${message}`);
4743
+ if (this.#backendStderrTail.length > 0) {
4744
+ this.logger.error(`[backend-recovery] recent backend stderr:
4745
+ ${this.#backendStderrTail.join(`
4746
+ `)}`);
4747
+ }
4748
+ const abandonedStatus = this.#recordBackendBuildStatus({ ok: false, files: [], message });
4749
+ this.#sendOrQueueBuildStatus(abandonedStatus);
4750
+ return;
4751
+ }
4726
4752
  this.#setBackendLifecycleState("recovering", reason);
4727
4753
  const attempt = this.#backendRecoveryAttempts;
4728
4754
  const delay = Math.min(BACKEND_RECOVERY_BASE_DELAY_MS * 2 ** attempt, BACKEND_RECOVERY_MAX_DELAY_MS);
@@ -4771,6 +4797,7 @@ ${this.#backendStderrTail.join(`
4771
4797
  this.#sendToBackend(message);
4772
4798
  }
4773
4799
  async#handleInvalidate(message) {
4800
+ this.#logDevPlan(message);
4774
4801
  if (shouldRestartBuilderByDevPlan(message)) {
4775
4802
  try {
4776
4803
  await this.#restartDevChildren(message);
@@ -4882,12 +4909,16 @@ ${this.#backendStderrTail.join(`
4882
4909
  this.#sendToBackend({ type: "build-status", data: status });
4883
4910
  }
4884
4911
  }
4912
+ #logDevPlan(message) {
4913
+ if (!message.devPlan)
4914
+ return;
4915
+ const { generation, roles, actions, reasonByFile } = message.devPlan;
4916
+ this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
4917
+ }
4885
4918
  async#shouldRestartBackend(message) {
4886
4919
  if (message.kinds.length === 1 && message.kinds[0] === "css")
4887
4920
  return false;
4888
4921
  if (message.devPlan) {
4889
- const { generation, roles, actions, reasonByFile } = message.devPlan;
4890
- this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
4891
4922
  const shouldRestart = shouldRestartBackendByDevPlan(message) ?? false;
4892
4923
  if (shouldRestart && message.kinds.includes("code"))
4893
4924
  await this.#backendGraph.refresh();
@@ -17269,14 +17300,18 @@ ${cssText}`).toString(36);
17269
17300
  }
17270
17301
  if (indexSync.changedFiles.length > 0)
17271
17302
  this.#sendBuildStatus("barrel", { generation, ok: true, files });
17272
- if (kinds.includes("code") && await this.batchMayChangePageKeys(appDir, expandedBatch)) {
17303
+ const rebuildClient = devPlan.actions.includes("rebuild-client");
17304
+ if (kinds.includes("code") && !rebuildClient) {
17305
+ this.#logger.verbose(`client rebuild skipped; devPlan actions=${devPlan.actions.join(",") || "(none)"}`);
17306
+ }
17307
+ if (kinds.includes("code") && rebuildClient && await this.batchMayChangePageKeys(appDir, expandedBatch)) {
17273
17308
  const started = Date.now();
17274
17309
  await this.#app.getPageKeys({ refresh: true });
17275
17310
  this.#logger.verbose(`pageKeys updated, app pageKeys are refreshed (${Date.now() - started}ms)`);
17276
- } else if (kinds.includes("code") && this.batchTouchesPagesTree(appDir, expandedBatch)) {
17311
+ } else if (kinds.includes("code") && rebuildClient && this.batchTouchesPagesTree(appDir, expandedBatch)) {
17277
17312
  this.#logger.verbose("pageKeys refresh skipped; changed page source cannot add/remove a route key");
17278
17313
  }
17279
- if (kinds.includes("code") && this.#shouldRebuildCsr()) {
17314
+ if (kinds.includes("code") && rebuildClient && this.#shouldRebuildCsr()) {
17280
17315
  try {
17281
17316
  const started = Date.now();
17282
17317
  await new CsrArtifactBuilder(this.#app).build();
@@ -17287,11 +17322,11 @@ ${cssText}`).toString(36);
17287
17322
  this.#logger.error(`csr-rebundle failed: ${message}`);
17288
17323
  this.#sendBuildStatus("csr", { generation, ok: false, files, message });
17289
17324
  }
17290
- } else if (kinds.includes("code")) {
17325
+ } else if (kinds.includes("code") && rebuildClient) {
17291
17326
  this.#logger.verbose(`csr-rebundle skipped; set AKAN_DEV_CSR_REBUILD=1 to enable per-save CSR rebuilds`);
17292
17327
  }
17293
17328
  process.send?.(event);
17294
- if (kinds.includes("code")) {
17329
+ if (kinds.includes("code") && rebuildClient) {
17295
17330
  try {
17296
17331
  const started = Date.now();
17297
17332
  const next = await new PagesBundleBuilder(this.#app).build();
@@ -17326,6 +17361,10 @@ ${cssText}`).toString(36);
17326
17361
  return;
17327
17362
  }
17328
17363
  });
17364
+ process.on("disconnect", () => {
17365
+ this.#logger.warn("host IPC channel closed; exiting builder");
17366
+ process.exit(0);
17367
+ });
17329
17368
  if (this.#watch)
17330
17369
  await this.installWatcher();
17331
17370
  process.send?.({ type: "builder-ready" });
package/index.js CHANGED
@@ -4285,9 +4285,10 @@ class IncrementalBuilderHost {
4285
4285
  // pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
4286
4286
  var backendMsgTypeSet = new Set(["build-route"]);
4287
4287
  var BACKEND_RESTART_DEBOUNCE_MS = 120;
4288
- var BACKEND_GRACEFUL_TIMEOUT_MS = 3000;
4288
+ var BACKEND_GRACEFUL_TIMEOUT_MS = 8000;
4289
4289
  var BACKEND_RECOVERY_BASE_DELAY_MS = 1000;
4290
4290
  var BACKEND_RECOVERY_MAX_DELAY_MS = 30000;
4291
+ var BACKEND_RECOVERY_MAX_ATTEMPTS = 5;
4291
4292
  var BACKEND_STDERR_TAIL_LIMIT = 40;
4292
4293
  var BUILDER_READY_TIMEOUT_MS = 150000;
4293
4294
  var BUILDER_START_MAX_ATTEMPTS = 3;
@@ -4312,6 +4313,8 @@ var shouldRestartBackendByDevPlan = (message) => {
4312
4313
  return message.devPlan.actions.includes("restart-backend");
4313
4314
  };
4314
4315
  var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
4316
+ var shouldAbandonBackendRecovery = (attempts, maxAttempts = BACKEND_RECOVERY_MAX_ATTEMPTS) => attempts >= maxAttempts;
4317
+ var normalizeBackendReportedGeneration = (generation) => generation >= 0 ? generation : undefined;
4315
4318
  var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
4316
4319
  var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
4317
4320
  var generationValue = (generation) => generation ?? -1;
@@ -4560,6 +4563,16 @@ class AkanAppHost {
4560
4563
  this.#replayBuilderState();
4561
4564
  return;
4562
4565
  }
4566
+ if (msg.type === "build-status") {
4567
+ const status = this.#recordBackendBuildStatus({
4568
+ generation: normalizeBackendReportedGeneration(msg.data.generation),
4569
+ ok: msg.data.ok,
4570
+ files: msg.data.files,
4571
+ message: msg.data.message
4572
+ });
4573
+ this.#sendOrQueueBuildStatus(status);
4574
+ return;
4575
+ }
4563
4576
  if (backendMsgTypeSet.has(msg.type))
4564
4577
  this.#sendToBuilder(msg);
4565
4578
  },
@@ -4721,6 +4734,19 @@ class AkanAppHost {
4721
4734
  #scheduleBackendRecovery(reason) {
4722
4735
  if (this.#backendRecoveryTimer || this.#backend)
4723
4736
  return;
4737
+ if (shouldAbandonBackendRecovery(this.#backendRecoveryAttempts)) {
4738
+ const message = `Backend exited ${this.#backendRecoveryAttempts} times in a row (${reason}); waiting for a server-side edit to retry.`;
4739
+ this.#setBackendLifecycleState("stopped", `gave up after ${this.#backendRecoveryAttempts} recovery attempts`);
4740
+ this.logger.error(`[backend-recovery] ${message}`);
4741
+ if (this.#backendStderrTail.length > 0) {
4742
+ this.logger.error(`[backend-recovery] recent backend stderr:
4743
+ ${this.#backendStderrTail.join(`
4744
+ `)}`);
4745
+ }
4746
+ const abandonedStatus = this.#recordBackendBuildStatus({ ok: false, files: [], message });
4747
+ this.#sendOrQueueBuildStatus(abandonedStatus);
4748
+ return;
4749
+ }
4724
4750
  this.#setBackendLifecycleState("recovering", reason);
4725
4751
  const attempt = this.#backendRecoveryAttempts;
4726
4752
  const delay = Math.min(BACKEND_RECOVERY_BASE_DELAY_MS * 2 ** attempt, BACKEND_RECOVERY_MAX_DELAY_MS);
@@ -4769,6 +4795,7 @@ ${this.#backendStderrTail.join(`
4769
4795
  this.#sendToBackend(message);
4770
4796
  }
4771
4797
  async#handleInvalidate(message) {
4798
+ this.#logDevPlan(message);
4772
4799
  if (shouldRestartBuilderByDevPlan(message)) {
4773
4800
  try {
4774
4801
  await this.#restartDevChildren(message);
@@ -4880,12 +4907,16 @@ ${this.#backendStderrTail.join(`
4880
4907
  this.#sendToBackend({ type: "build-status", data: status });
4881
4908
  }
4882
4909
  }
4910
+ #logDevPlan(message) {
4911
+ if (!message.devPlan)
4912
+ return;
4913
+ const { generation, roles, actions, reasonByFile } = message.devPlan;
4914
+ this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
4915
+ }
4883
4916
  async#shouldRestartBackend(message) {
4884
4917
  if (message.kinds.length === 1 && message.kinds[0] === "css")
4885
4918
  return false;
4886
4919
  if (message.devPlan) {
4887
- const { generation, roles, actions, reasonByFile } = message.devPlan;
4888
- this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
4889
4920
  const shouldRestart = shouldRestartBackendByDevPlan(message) ?? false;
4890
4921
  if (shouldRestart && message.kinds.includes("code"))
4891
4922
  await this.#backendGraph.refresh();
@@ -22756,14 +22787,18 @@ class WorkspaceScript extends script("workspace", [
22756
22787
  ApplicationScript,
22757
22788
  LibraryScript,
22758
22789
  PackageScript,
22759
- CloudScript
22790
+ CloudScript,
22791
+ ContextScript,
22792
+ AgentScript
22760
22793
  ]) {
22761
22794
  async createWorkspace(repoName, appName, {
22762
22795
  dirname: dirname2 = ".",
22763
22796
  installLibs = false,
22764
22797
  init = true,
22765
22798
  registryUrl,
22766
- owner
22799
+ owner,
22800
+ mcpInstall = true,
22801
+ agentInstall = true
22767
22802
  }) {
22768
22803
  const akanVersion = await this.packageScript.version(null, { log: false });
22769
22804
  const workspace = await this.workspaceRunner.createWorkspace(repoName, appName, {
@@ -22784,6 +22819,10 @@ class WorkspaceScript extends script("workspace", [
22784
22819
  dict: { appName },
22785
22820
  options: { libs: installLibs ? ["util", "shared"] : [] }
22786
22821
  });
22822
+ if (agentInstall)
22823
+ await this.agentScript.agent(workspace, "install", "all", { force: true });
22824
+ if (mcpInstall)
22825
+ await this.contextScript.mcpInstall(workspace, "all", { force: true });
22787
22826
  const gitSpinner = workspace.spinning("Initializing git repository and commit...");
22788
22827
  try {
22789
22828
  await workspace.commit("Initial commit", { init: true });
@@ -22882,13 +22921,22 @@ class WorkspaceCommand extends command("workspace", [WorkspaceScript], ({ public
22882
22921
  desc: "owner of the workspace",
22883
22922
  default: process.env.GITHUB_OWNER,
22884
22923
  nullable: true
22885
- }).exec(async function(workspaceName, app, dir, libs, init, registry, owner) {
22924
+ }).option("mcpInstall", Boolean, {
22925
+ desc: "Install the Akan MCP server config for Cursor, Claude Code, and Codex? (Recommended)",
22926
+ default: true
22927
+ }).option("agentInstall", Boolean, {
22928
+ flag: "A",
22929
+ desc: "Install Akan agent rules (AGENTS.md, CLAUDE.md, Cursor)? (Recommended)",
22930
+ default: true
22931
+ }).exec(async function(workspaceName, app, dir, libs, init, registry, owner, mcpInstall, agentInstall) {
22886
22932
  const appName = app || "app";
22887
22933
  await this.workspaceScript.createWorkspace(workspaceName.toLowerCase().replace(/ /g, "-"), appName.toLowerCase().replace(/ /g, "-"), {
22888
22934
  dirname: dir,
22889
22935
  installLibs: libs,
22890
22936
  init,
22891
22937
  owner,
22938
+ mcpInstall,
22939
+ agentInstall,
22892
22940
  ...registry ? { registryUrl: registry } : {}
22893
22941
  });
22894
22942
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "2.4.0-rc.0",
3
+ "version": "2.4.0-rc.1",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -34,7 +34,7 @@
34
34
  "@langchain/openai": "^1.4.6",
35
35
  "@tailwindcss/node": "^4.3.0",
36
36
  "@trapezedev/project": "^7.1.4",
37
- "akanjs": "2.4.0-rc.0",
37
+ "akanjs": "2.4.0-rc.1",
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3",
40
40
  "daisyui": "5.5.23",
@@ -28,6 +28,18 @@
28
28
  --color-error-content: #fafafa;
29
29
  --color-open: #27ae60;
30
30
  --color-open-content: #fafafa;
31
+
32
+ /* Shape & feel — customize these too, not just colors, to make the theme yours.
33
+ These are daisyUI v5 theme variables; the values below are the neutral defaults.
34
+ See `akan guideline show cssRule` and https://daisyui.com/docs/themes/ for the full set. */
35
+ --radius-selector: 1rem; /* checkbox, toggle, badge corner rounding */
36
+ --radius-field: 0.5rem; /* button, input, select, tab corner rounding */
37
+ --radius-box: 1rem; /* card, modal, alert corner rounding */
38
+ --size-selector: 0.25rem; /* base scale (density) for selector controls */
39
+ --size-field: 0.25rem; /* base scale (density) for field controls */
40
+ --border: 1px; /* component border width */
41
+ --depth: 0; /* 1 = subtle 3D depth on components */
42
+ --noise: 0; /* 1 = grain texture on surfaces */
31
43
  }
32
44
 
33
45
  @plugin "daisyui/theme" {
@@ -51,4 +63,14 @@
51
63
  --color-error: #f02020;
52
64
  --color-error-content: #ffffff;
53
65
  --color-open: #10fc00;
66
+
67
+ /* Keep shape & feel consistent with the light theme (usually the same values across modes). */
68
+ --radius-selector: 1rem;
69
+ --radius-field: 0.5rem;
70
+ --radius-box: 1rem;
71
+ --size-selector: 0.25rem;
72
+ --size-field: 0.25rem;
73
+ --border: 1px;
74
+ --depth: 0;
75
+ --noise: 0;
54
76
  }