@quilltap/theme-storybook 1.0.60 → 1.0.62

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to `@quilltap/theme-storybook` will be documented in this file.
4
4
 
5
+ ## [1.0.62] - 2026-08-25
6
+
7
+ ### Added
8
+ - `qt-text-on-primary`, `qt-text-on-success`, and `qt-text-on-destructive`: the rest of the on-a-filled-surface foreground family that `qt-text-on-accent` started, each mapping to the theme's matching `*Foreground` token. Plus the hand-written hover partners `hover:qt-text-on-accent/-primary/-success/-destructive`, for controls that sit quiet at rest and take a filled background on hover. Mirrors the app-side definitions.
9
+ - `Surfaces` story: a "Foregrounds on filled surfaces" section previewing all four fills with their foregrounds, and a hover row for the hover partners. It also spells out the naming trap — the classes are `-on-<fill>`, never `-<fill>-foreground`, which is the Tailwind spelling and no `qt-` class at all.
10
+
5
11
  ## [1.0.44] - 2026-06-14
6
12
 
7
13
  ### Added
@@ -1043,6 +1043,11 @@ var note = {
1043
1043
  };
1044
1044
  var Surfaces = () => {
1045
1045
  return /* @__PURE__ */ jsxs9("div", { style: { padding: "1.5rem" }, children: [
1046
+ /* @__PURE__ */ jsx9("style", { children: `
1047
+ .surfaces-demo-fill-primary:hover { background-color: var(--color-primary); }
1048
+ .surfaces-demo-fill-success:hover { background-color: var(--color-success); }
1049
+ .surfaces-demo-fill-destructive:hover { background-color: var(--color-destructive); }
1050
+ ` }),
1046
1051
  /* @__PURE__ */ jsx9("h2", { style: { fontSize: "1.5rem", fontWeight: 700, marginBottom: "0.5rem" }, children: "Surfaces" }),
1047
1052
  /* @__PURE__ */ jsxs9("p", { style: { ...note, maxWidth: "44rem" }, children: [
1048
1053
  "How accent-coloured surfaces stay legible no matter how bold a theme makes its ",
@@ -1079,6 +1084,53 @@ var Surfaces = () => {
1079
1084
  /* @__PURE__ */ jsx9("kbd", { className: "qt-bg-accent qt-text-on-accent", style: { padding: "0.125rem 0.375rem", borderRadius: "0.25rem", fontSize: "0.75rem" }, children: "\u21B5" })
1080
1085
  ] })
1081
1086
  ] }),
1087
+ /* @__PURE__ */ jsxs9("section", { style: { marginBottom: "2rem" }, children: [
1088
+ /* @__PURE__ */ jsxs9("h3", { style: sectionHeading2, children: [
1089
+ "Foregrounds on filled surfaces \u2014 the ",
1090
+ /* @__PURE__ */ jsx9("code", { children: "qt-text-on-*" }),
1091
+ " family"
1092
+ ] }),
1093
+ /* @__PURE__ */ jsxs9("p", { style: note, children: [
1094
+ "The same contract, extended to every filled surface the app paints:",
1095
+ " ",
1096
+ /* @__PURE__ */ jsx9("code", { children: "qt-text-on-accent" }),
1097
+ ", ",
1098
+ /* @__PURE__ */ jsx9("code", { children: "qt-text-on-primary" }),
1099
+ ",",
1100
+ " ",
1101
+ /* @__PURE__ */ jsx9("code", { children: "qt-text-on-success" }),
1102
+ ", and ",
1103
+ /* @__PURE__ */ jsx9("code", { children: "qt-text-on-destructive" }),
1104
+ ", each mapping to the theme's matching ",
1105
+ /* @__PURE__ */ jsx9("code", { children: "*Foreground" }),
1106
+ " token. Note the naming \u2014 ",
1107
+ /* @__PURE__ */ jsxs9("code", { children: [
1108
+ "-on-",
1109
+ /* @__PURE__ */ jsx9("em", { children: "fill" })
1110
+ ] }),
1111
+ ", never ",
1112
+ /* @__PURE__ */ jsxs9("code", { children: [
1113
+ "-",
1114
+ /* @__PURE__ */ jsx9("em", { children: "fill" }),
1115
+ "-foreground"
1116
+ ] }),
1117
+ ", which is the Tailwind spelling and is not a ",
1118
+ /* @__PURE__ */ jsx9("code", { children: "qt-" }),
1119
+ " class at all."
1120
+ ] }),
1121
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.75rem", alignItems: "center" }, children: [
1122
+ /* @__PURE__ */ jsx9("span", { className: "qt-bg-accent qt-text-on-accent", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on accent" }),
1123
+ /* @__PURE__ */ jsx9("span", { className: "qt-bg-primary qt-text-on-primary", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on primary" }),
1124
+ /* @__PURE__ */ jsx9("span", { className: "qt-bg-success qt-text-on-success", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on success" }),
1125
+ /* @__PURE__ */ jsx9("span", { className: "qt-bg-destructive qt-text-on-destructive", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on destructive" })
1126
+ ] }),
1127
+ /* @__PURE__ */ jsx9("p", { style: { ...note, marginTop: "1rem" }, children: "Each has a hover partner for controls that stay quiet at rest and take the fill on hover. Tailwind generates no variants for these classes, so the hover forms are written out by hand \u2014 reach for one that was never written and it is simply inert. Hover the buttons below." }),
1128
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.75rem", alignItems: "center" }, children: [
1129
+ /* @__PURE__ */ jsx9("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-primary hover:qt-text-on-primary", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-primary" }),
1130
+ /* @__PURE__ */ jsx9("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-success hover:qt-text-on-success", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-success" }),
1131
+ /* @__PURE__ */ jsx9("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-destructive hover:qt-text-on-destructive", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-destructive" })
1132
+ ] })
1133
+ ] }),
1082
1134
  /* @__PURE__ */ jsxs9("section", { style: { marginBottom: "2rem" }, children: [
1083
1135
  /* @__PURE__ */ jsxs9("h3", { style: sectionHeading2, children: [
1084
1136
  "Hover-highlight rows \u2014 ",
package/dist/index.css CHANGED
@@ -286,6 +286,10 @@ body {
286
286
  --qt-status-danger-bg: color-mix(in srgb, var(--color-destructive) 85%, transparent);
287
287
  --qt-status-danger-fg: var(--color-destructive-foreground);
288
288
  --qt-status-danger-border: var(--color-destructive);
289
+ --qt-badge-primary-bg: var(--color-primary);
290
+ --qt-badge-primary-fg: var(--color-primary-foreground);
291
+ --qt-badge-secondary-bg: var(--color-secondary);
292
+ --qt-badge-secondary-fg: var(--color-secondary-foreground);
289
293
  --qt-alert-success-bg: hsl(142 76% 36% / 0.1);
290
294
  --qt-alert-success-fg: hsl(142 76% 30%);
291
295
  --qt-alert-success-border: hsl(142 76% 36% / 0.3);
@@ -888,6 +892,45 @@ body {
888
892
  color: var(--color-foreground);
889
893
  border: 1px solid var(--color-border);
890
894
  }
895
+ .qt-badge-provider-openai,
896
+ .qt-badge-provider-ollama,
897
+ .qt-badge-provider-openrouter,
898
+ .qt-badge-provider-nanogpt,
899
+ .qt-badge-provider-builtin {
900
+ display: inline-flex;
901
+ align-items: center;
902
+ justify-content: center;
903
+ flex-shrink: 0;
904
+ padding: var(--qt-badge-padding-y) var(--qt-badge-padding-x);
905
+ border-radius: var(--qt-badge-radius);
906
+ font-size: var(--qt-badge-font-size);
907
+ font-weight: var(--qt-badge-font-weight);
908
+ }
909
+ .qt-badge-provider-openai {
910
+ background: var(--qt-status-success-bg);
911
+ color: var(--qt-status-success-fg);
912
+ border: 1px solid var(--qt-status-success-border);
913
+ }
914
+ .qt-badge-provider-ollama {
915
+ background: var(--qt-badge-secondary-bg);
916
+ color: var(--qt-badge-secondary-fg);
917
+ border: 1px solid var(--qt-badge-secondary-border);
918
+ }
919
+ .qt-badge-provider-openrouter {
920
+ background: var(--qt-status-warning-bg);
921
+ color: var(--qt-status-warning-fg);
922
+ border: 1px solid var(--qt-status-warning-border);
923
+ }
924
+ .qt-badge-provider-nanogpt {
925
+ background: var(--qt-badge-primary-bg);
926
+ color: var(--qt-badge-primary-fg);
927
+ border: 1px solid var(--qt-badge-primary-border);
928
+ }
929
+ .qt-badge-provider-builtin {
930
+ background: var(--qt-status-info-bg);
931
+ color: var(--qt-status-info-fg);
932
+ border: 1px solid var(--qt-status-info-border);
933
+ }
891
934
  .qt-badge-wardrobe-top,
892
935
  .qt-badge-wardrobe-bottom,
893
936
  .qt-badge-wardrobe-footwear,
@@ -1905,6 +1948,15 @@ body {
1905
1948
  .qt-text-on-accent {
1906
1949
  color: var(--color-accent-foreground);
1907
1950
  }
1951
+ .qt-text-on-primary {
1952
+ color: var(--color-primary-foreground);
1953
+ }
1954
+ .qt-text-on-success {
1955
+ color: var(--color-success-foreground);
1956
+ }
1957
+ .qt-text-on-destructive {
1958
+ color: var(--color-destructive-foreground);
1959
+ }
1908
1960
  .qt-hover-accent {
1909
1961
  transition: background-color 0.15s ease, color 0.15s ease;
1910
1962
  }
@@ -2035,6 +2087,18 @@ body {
2035
2087
  .hover\:qt-bg-primary\/10:hover {
2036
2088
  background-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
2037
2089
  }
2090
+ .hover\:qt-text-on-accent:hover {
2091
+ color: var(--color-accent-foreground);
2092
+ }
2093
+ .hover\:qt-text-on-primary:hover {
2094
+ color: var(--color-primary-foreground);
2095
+ }
2096
+ .hover\:qt-text-on-success:hover {
2097
+ color: var(--color-success-foreground);
2098
+ }
2099
+ .hover\:qt-text-on-destructive:hover {
2100
+ color: var(--color-destructive-foreground);
2101
+ }
2038
2102
  .qt-bg-overlay {
2039
2103
  background-color: var(--qt-overlay-bg, rgb(0 0 0 / 0.85));
2040
2104
  }
package/dist/index.js CHANGED
@@ -1153,6 +1153,11 @@ var note = {
1153
1153
  };
1154
1154
  var Surfaces = () => {
1155
1155
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { padding: "1.5rem" }, children: [
1156
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("style", { children: `
1157
+ .surfaces-demo-fill-primary:hover { background-color: var(--color-primary); }
1158
+ .surfaces-demo-fill-success:hover { background-color: var(--color-success); }
1159
+ .surfaces-demo-fill-destructive:hover { background-color: var(--color-destructive); }
1160
+ ` }),
1156
1161
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { style: { fontSize: "1.5rem", fontWeight: 700, marginBottom: "0.5rem" }, children: "Surfaces" }),
1157
1162
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { style: { ...note, maxWidth: "44rem" }, children: [
1158
1163
  "How accent-coloured surfaces stay legible no matter how bold a theme makes its ",
@@ -1189,6 +1194,53 @@ var Surfaces = () => {
1189
1194
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("kbd", { className: "qt-bg-accent qt-text-on-accent", style: { padding: "0.125rem 0.375rem", borderRadius: "0.25rem", fontSize: "0.75rem" }, children: "\u21B5" })
1190
1195
  ] })
1191
1196
  ] }),
1197
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("h3", { style: sectionHeading2, children: [
1199
+ "Foregrounds on filled surfaces \u2014 the ",
1200
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: "qt-text-on-*" }),
1201
+ " family"
1202
+ ] }),
1203
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { style: note, children: [
1204
+ "The same contract, extended to every filled surface the app paints:",
1205
+ " ",
1206
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: "qt-text-on-accent" }),
1207
+ ", ",
1208
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: "qt-text-on-primary" }),
1209
+ ",",
1210
+ " ",
1211
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: "qt-text-on-success" }),
1212
+ ", and ",
1213
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: "qt-text-on-destructive" }),
1214
+ ", each mapping to the theme's matching ",
1215
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: "*Foreground" }),
1216
+ " token. Note the naming \u2014 ",
1217
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("code", { children: [
1218
+ "-on-",
1219
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("em", { children: "fill" })
1220
+ ] }),
1221
+ ", never ",
1222
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("code", { children: [
1223
+ "-",
1224
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("em", { children: "fill" }),
1225
+ "-foreground"
1226
+ ] }),
1227
+ ", which is the Tailwind spelling and is not a ",
1228
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: "qt-" }),
1229
+ " class at all."
1230
+ ] }),
1231
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.75rem", alignItems: "center" }, children: [
1232
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "qt-bg-accent qt-text-on-accent", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on accent" }),
1233
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "qt-bg-primary qt-text-on-primary", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on primary" }),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "qt-bg-success qt-text-on-success", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on success" }),
1235
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "qt-bg-destructive qt-text-on-destructive", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on destructive" })
1236
+ ] }),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { ...note, marginTop: "1rem" }, children: "Each has a hover partner for controls that stay quiet at rest and take the fill on hover. Tailwind generates no variants for these classes, so the hover forms are written out by hand \u2014 reach for one that was never written and it is simply inert. Hover the buttons below." }),
1238
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.75rem", alignItems: "center" }, children: [
1239
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-primary hover:qt-text-on-primary", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-primary" }),
1240
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-success hover:qt-text-on-success", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-success" }),
1241
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-destructive hover:qt-text-on-destructive", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-destructive" })
1242
+ ] })
1243
+ ] }),
1192
1244
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
1193
1245
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("h3", { style: sectionHeading2, children: [
1194
1246
  "Hover-highlight rows \u2014 ",
package/dist/index.mjs CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  ThemeComparison,
22
22
  Typography,
23
23
  Workspace
24
- } from "./chunk-XSNC4J5T.mjs";
24
+ } from "./chunk-TESCTXU3.mjs";
25
25
  import "./chunk-WUKYLWAZ.mjs";
26
26
 
27
27
  // src/preview.tsx
@@ -1090,6 +1090,11 @@ var note = {
1090
1090
  };
1091
1091
  var Surfaces = () => {
1092
1092
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { padding: "1.5rem" }, children: [
1093
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("style", { children: `
1094
+ .surfaces-demo-fill-primary:hover { background-color: var(--color-primary); }
1095
+ .surfaces-demo-fill-success:hover { background-color: var(--color-success); }
1096
+ .surfaces-demo-fill-destructive:hover { background-color: var(--color-destructive); }
1097
+ ` }),
1093
1098
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: { fontSize: "1.5rem", fontWeight: 700, marginBottom: "0.5rem" }, children: "Surfaces" }),
1094
1099
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { style: { ...note, maxWidth: "44rem" }, children: [
1095
1100
  "How accent-coloured surfaces stay legible no matter how bold a theme makes its ",
@@ -1126,6 +1131,53 @@ var Surfaces = () => {
1126
1131
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("kbd", { className: "qt-bg-accent qt-text-on-accent", style: { padding: "0.125rem 0.375rem", borderRadius: "0.25rem", fontSize: "0.75rem" }, children: "\u21B5" })
1127
1132
  ] })
1128
1133
  ] }),
1134
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("h3", { style: sectionHeading2, children: [
1136
+ "Foregrounds on filled surfaces \u2014 the ",
1137
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: "qt-text-on-*" }),
1138
+ " family"
1139
+ ] }),
1140
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { style: note, children: [
1141
+ "The same contract, extended to every filled surface the app paints:",
1142
+ " ",
1143
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: "qt-text-on-accent" }),
1144
+ ", ",
1145
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: "qt-text-on-primary" }),
1146
+ ",",
1147
+ " ",
1148
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: "qt-text-on-success" }),
1149
+ ", and ",
1150
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: "qt-text-on-destructive" }),
1151
+ ", each mapping to the theme's matching ",
1152
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: "*Foreground" }),
1153
+ " token. Note the naming \u2014 ",
1154
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("code", { children: [
1155
+ "-on-",
1156
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("em", { children: "fill" })
1157
+ ] }),
1158
+ ", never ",
1159
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("code", { children: [
1160
+ "-",
1161
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("em", { children: "fill" }),
1162
+ "-foreground"
1163
+ ] }),
1164
+ ", which is the Tailwind spelling and is not a ",
1165
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("code", { children: "qt-" }),
1166
+ " class at all."
1167
+ ] }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.75rem", alignItems: "center" }, children: [
1169
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "qt-bg-accent qt-text-on-accent", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on accent" }),
1170
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "qt-bg-primary qt-text-on-primary", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on primary" }),
1171
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "qt-bg-success qt-text-on-success", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on success" }),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "qt-bg-destructive qt-text-on-destructive", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600 }, children: "on destructive" })
1173
+ ] }),
1174
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { ...note, marginTop: "1rem" }, children: "Each has a hover partner for controls that stay quiet at rest and take the fill on hover. Tailwind generates no variants for these classes, so the hover forms are written out by hand \u2014 reach for one that was never written and it is simply inert. Hover the buttons below." }),
1175
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "0.75rem", alignItems: "center" }, children: [
1176
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-primary hover:qt-text-on-primary", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-primary" }),
1177
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-success hover:qt-text-on-success", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-success" }),
1178
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "qt-bg-card qt-text-secondary surfaces-demo-fill-destructive hover:qt-text-on-destructive", style: { padding: "0.25rem 0.625rem", borderRadius: "0.375rem", fontSize: "0.8125rem", fontWeight: 600, border: "1px solid var(--color-border)", cursor: "pointer" }, children: "hover:qt-text-on-destructive" })
1179
+ ] })
1180
+ ] }),
1129
1181
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { style: { marginBottom: "2rem" }, children: [
1130
1182
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("h3", { style: sectionHeading2, children: [
1131
1183
  "Hover-highlight rows \u2014 ",
@@ -21,7 +21,7 @@ import {
21
21
  ThemeComparison,
22
22
  Typography,
23
23
  Workspace
24
- } from "../chunk-XSNC4J5T.mjs";
24
+ } from "../chunk-TESCTXU3.mjs";
25
25
  import "../chunk-WUKYLWAZ.mjs";
26
26
  export {
27
27
  Avatars,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quilltap/theme-storybook",
3
- "version": "1.0.60",
3
+ "version": "1.0.62",
4
4
  "description": "Storybook preset and stories for developing Quilltap theme plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -65,7 +65,7 @@
65
65
  "license": "MIT",
66
66
  "repository": {
67
67
  "type": "git",
68
- "url": "https://github.com/foundry-9/quilltap-server.git",
68
+ "url": "git+https://github.com/foundry-9/quilltap-server.git",
69
69
  "directory": "packages/theme-storybook"
70
70
  },
71
71
  "homepage": "https://github.com/foundry-9/quilltap-server/tree/main/packages/theme-storybook#readme",
@@ -160,6 +160,12 @@
160
160
  --qt-status-danger-fg: var(--color-destructive-foreground);
161
161
  --qt-status-danger-border: var(--color-destructive);
162
162
 
163
+ /* Badge palette tokens - referenced by the provider badges */
164
+ --qt-badge-primary-bg: var(--color-primary);
165
+ --qt-badge-primary-fg: var(--color-primary-foreground);
166
+ --qt-badge-secondary-bg: var(--color-secondary);
167
+ --qt-badge-secondary-fg: var(--color-secondary-foreground);
168
+
163
169
  /* Alert tokens - semi-transparent backgrounds with readable text */
164
170
  --qt-alert-success-bg: hsl(142 76% 36% / 0.1);
165
171
  --qt-alert-success-fg: hsl(142 76% 30%);
@@ -919,6 +925,80 @@
919
925
  border: 1px solid var(--color-border);
920
926
  }
921
927
 
928
+ /* ==========================================================================
929
+ PROVIDER BADGES
930
+ ========================================================================== */
931
+
932
+ /**
933
+ * Shared geometry for the provider badges (embedding-profile provider chips).
934
+ * These do NOT build on .qt-badge in the app — they carry their own box, so
935
+ * the rule below is self-contained (and, mirroring the app, sets no
936
+ * line-height).
937
+ */
938
+ .qt-badge-provider-openai,
939
+ .qt-badge-provider-ollama,
940
+ .qt-badge-provider-openrouter,
941
+ .qt-badge-provider-nanogpt,
942
+ .qt-badge-provider-builtin {
943
+ display: inline-flex;
944
+ align-items: center;
945
+ justify-content: center;
946
+ flex-shrink: 0;
947
+ padding: var(--qt-badge-padding-y) var(--qt-badge-padding-x);
948
+ border-radius: var(--qt-badge-radius);
949
+ font-size: var(--qt-badge-font-size);
950
+ font-weight: var(--qt-badge-font-weight);
951
+ }
952
+
953
+ /**
954
+ * OpenAI provider badge (green/success semantic)
955
+ */
956
+ .qt-badge-provider-openai {
957
+ background: var(--qt-status-success-bg);
958
+ color: var(--qt-status-success-fg);
959
+ border: 1px solid var(--qt-status-success-border);
960
+ }
961
+
962
+ /**
963
+ * Ollama provider badge (secondary/muted)
964
+ * Mirrors the app, quirk included: --qt-badge-secondary-border has no default
965
+ * definition, so the border only renders in themes that define it.
966
+ */
967
+ .qt-badge-provider-ollama {
968
+ background: var(--qt-badge-secondary-bg);
969
+ color: var(--qt-badge-secondary-fg);
970
+ border: 1px solid var(--qt-badge-secondary-border);
971
+ }
972
+
973
+ /**
974
+ * OpenRouter provider badge (warning/orange semantic)
975
+ */
976
+ .qt-badge-provider-openrouter {
977
+ background: var(--qt-status-warning-bg);
978
+ color: var(--qt-status-warning-fg);
979
+ border: 1px solid var(--qt-status-warning-border);
980
+ }
981
+
982
+ /**
983
+ * NanoGPT provider badge (primary/accent semantic)
984
+ * Same border quirk as the ollama badge: --qt-badge-primary-border has no
985
+ * default definition.
986
+ */
987
+ .qt-badge-provider-nanogpt {
988
+ background: var(--qt-badge-primary-bg);
989
+ color: var(--qt-badge-primary-fg);
990
+ border: 1px solid var(--qt-badge-primary-border);
991
+ }
992
+
993
+ /**
994
+ * Built-in provider badge (info/blue semantic)
995
+ */
996
+ .qt-badge-provider-builtin {
997
+ background: var(--qt-status-info-bg);
998
+ color: var(--qt-status-info-fg);
999
+ border: 1px solid var(--qt-status-info-border);
1000
+ }
1001
+
922
1002
  /* ==========================================================================
923
1003
  WARDROBE SLOT BADGES
924
1004
  ========================================================================== */
@@ -2274,10 +2354,32 @@
2274
2354
  background-color: var(--color-muted-foreground);
2275
2355
  }
2276
2356
 
2357
+ /* Foregrounds intended for use ON a filled surface — the paired counterparts of
2358
+ the accent/primary/success/destructive fills. Each maps to the token a theme
2359
+ defines for text sitting on that fill, so a badge or a solid button keeps its
2360
+ contrast on every theme instead of inheriting whatever colour the surrounding
2361
+ page happens to be using.
2362
+
2363
+ <span class="qt-bg-accent qt-text-on-accent">…</span>
2364
+ <button class="qt-bg-destructive qt-text-on-destructive">…</button>
2365
+
2366
+ Mind the naming: these are `-on-<fill>`, never `-<fill>-foreground`. */
2277
2367
  .qt-text-on-accent {
2278
2368
  color: var(--color-accent-foreground);
2279
2369
  }
2280
2370
 
2371
+ .qt-text-on-primary {
2372
+ color: var(--color-primary-foreground);
2373
+ }
2374
+
2375
+ .qt-text-on-success {
2376
+ color: var(--color-success-foreground);
2377
+ }
2378
+
2379
+ .qt-text-on-destructive {
2380
+ color: var(--color-destructive-foreground);
2381
+ }
2382
+
2281
2383
  /* Hoverable accent surface — list rows, menu items, and option cards that
2282
2384
  should highlight on hover. On hover it paints the accent background AND
2283
2385
  forces the accent foreground onto the row and every descendant, so the whole
@@ -2469,6 +2571,26 @@
2469
2571
  background-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
2470
2572
  }
2471
2573
 
2574
+ /* Hover foregrounds for filled surfaces — the hover partners of the
2575
+ qt-text-on-* family, for controls that sit quiet at rest and take a filled
2576
+ background on hover (`qt-text-secondary hover:qt-bg-success
2577
+ hover:qt-text-on-success`). */
2578
+ .hover\:qt-text-on-accent:hover {
2579
+ color: var(--color-accent-foreground);
2580
+ }
2581
+
2582
+ .hover\:qt-text-on-primary:hover {
2583
+ color: var(--color-primary-foreground);
2584
+ }
2585
+
2586
+ .hover\:qt-text-on-success:hover {
2587
+ color: var(--color-success-foreground);
2588
+ }
2589
+
2590
+ .hover\:qt-text-on-destructive:hover {
2591
+ color: var(--color-destructive-foreground);
2592
+ }
2593
+
2472
2594
  /* ==========================================================================
2473
2595
  OVERLAY UTILITIES
2474
2596
  For image modals, lightboxes, and overlay UI elements.
@@ -27,6 +27,14 @@ const note: React.CSSProperties = {
27
27
  export const Surfaces: React.FC = () => {
28
28
  return (
29
29
  <div style={{ padding: '1.5rem' }}>
30
+ {/* Story-local: the app pairs the hover foregrounds with Tailwind's
31
+ hover:bg-* fills, which don't exist here. Demo fills only — these are
32
+ not qt-* utilities and must not be copied into an app or a theme. */}
33
+ <style>{`
34
+ .surfaces-demo-fill-primary:hover { background-color: var(--color-primary); }
35
+ .surfaces-demo-fill-success:hover { background-color: var(--color-success); }
36
+ .surfaces-demo-fill-destructive:hover { background-color: var(--color-destructive); }
37
+ `}</style>
30
38
  <h2 style={{ fontSize: '1.5rem', fontWeight: 700, marginBottom: '0.5rem' }}>Surfaces</h2>
31
39
  <p style={{ ...note, maxWidth: '44rem' }}>
32
40
  How accent-coloured surfaces stay legible no matter how bold a theme makes
@@ -57,6 +65,50 @@ export const Surfaces: React.FC = () => {
57
65
  </div>
58
66
  </section>
59
67
 
68
+ {/* Foregrounds on filled surfaces */}
69
+ <section style={{ marginBottom: '2rem' }}>
70
+ <h3 style={sectionHeading}>Foregrounds on filled surfaces — the <code>qt-text-on-*</code> family</h3>
71
+ <p style={note}>
72
+ The same contract, extended to every filled surface the app paints:{' '}
73
+ <code>qt-text-on-accent</code>, <code>qt-text-on-primary</code>,{' '}
74
+ <code>qt-text-on-success</code>, and <code>qt-text-on-destructive</code>, each
75
+ mapping to the theme&apos;s matching <code>*Foreground</code> token. Note the
76
+ naming — <code>-on-<em>fill</em></code>, never <code>-<em>fill</em>-foreground</code>,
77
+ which is the Tailwind spelling and is not a <code>qt-</code> class at all.
78
+ </p>
79
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem', alignItems: 'center' }}>
80
+ <span className="qt-bg-accent qt-text-on-accent" style={{ padding: '0.25rem 0.625rem', borderRadius: '0.375rem', fontSize: '0.8125rem', fontWeight: 600 }}>
81
+ on accent
82
+ </span>
83
+ <span className="qt-bg-primary qt-text-on-primary" style={{ padding: '0.25rem 0.625rem', borderRadius: '0.375rem', fontSize: '0.8125rem', fontWeight: 600 }}>
84
+ on primary
85
+ </span>
86
+ <span className="qt-bg-success qt-text-on-success" style={{ padding: '0.25rem 0.625rem', borderRadius: '0.375rem', fontSize: '0.8125rem', fontWeight: 600 }}>
87
+ on success
88
+ </span>
89
+ <span className="qt-bg-destructive qt-text-on-destructive" style={{ padding: '0.25rem 0.625rem', borderRadius: '0.375rem', fontSize: '0.8125rem', fontWeight: 600 }}>
90
+ on destructive
91
+ </span>
92
+ </div>
93
+ <p style={{ ...note, marginTop: '1rem' }}>
94
+ Each has a hover partner for controls that stay quiet at rest and take the fill
95
+ on hover. Tailwind generates no variants for these classes, so the hover forms
96
+ are written out by hand — reach for one that was never written and it is simply
97
+ inert. Hover the buttons below.
98
+ </p>
99
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem', alignItems: 'center' }}>
100
+ <button className="qt-bg-card qt-text-secondary surfaces-demo-fill-primary hover:qt-text-on-primary" style={{ padding: '0.25rem 0.625rem', borderRadius: '0.375rem', fontSize: '0.8125rem', fontWeight: 600, border: '1px solid var(--color-border)', cursor: 'pointer' }}>
101
+ hover:qt-text-on-primary
102
+ </button>
103
+ <button className="qt-bg-card qt-text-secondary surfaces-demo-fill-success hover:qt-text-on-success" style={{ padding: '0.25rem 0.625rem', borderRadius: '0.375rem', fontSize: '0.8125rem', fontWeight: 600, border: '1px solid var(--color-border)', cursor: 'pointer' }}>
104
+ hover:qt-text-on-success
105
+ </button>
106
+ <button className="qt-bg-card qt-text-secondary surfaces-demo-fill-destructive hover:qt-text-on-destructive" style={{ padding: '0.25rem 0.625rem', borderRadius: '0.375rem', fontSize: '0.8125rem', fontWeight: 600, border: '1px solid var(--color-border)', cursor: 'pointer' }}>
107
+ hover:qt-text-on-destructive
108
+ </button>
109
+ </div>
110
+ </section>
111
+
60
112
  {/* Hover-highlight rows */}
61
113
  <section style={{ marginBottom: '2rem' }}>
62
114
  <h3 style={sectionHeading}>Hover-highlight rows — <code>qt-hover-accent</code></h3>