@nadicodeai/design-system 12.1.180 → 12.1.182

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/DESIGN.md CHANGED
@@ -558,7 +558,7 @@ The neutral spine carries grounds, chrome, text and every control: `{colors.ink}
558
558
 
559
559
  ### Colour as stage
560
560
 
561
- The identity palette is the brand's colour: cobalto `{colors.identity-cobalto}`, verde `{colors.identity-verde}`, arancio `{colors.identity-arancio}`, giallo `{colors.identity-giallo}` and rosso `{colors.identity-rosso}`, with `{colors.identity-ink}` and `{colors.identity-white}` as the poles type sits on. Azzurro `{colors.identity-azzurro}` has three jobs: the first stripe of Nadia's ribbon, the inner stripe of Alveare's mark ("Brand media authority"), and a band of a company's card foil ("Company card").
561
+ The identity palette is the brand's colour: cobalto `{colors.identity-cobalto}`, verde `{colors.identity-verde}`, arancio `{colors.identity-arancio}`, giallo `{colors.identity-giallo}` and rosso `{colors.identity-rosso}`, with `{colors.identity-ink}` and `{colors.identity-white}` as the poles type sits on. Azzurro `{colors.identity-azzurro}` has three jobs: the first stripe of Nadia's ribbon, the last cell of Alveare's mark and the last band of Alveare Server's ("Brand media authority"), and a band of a company's card foil ("Company card").
562
562
 
563
563
  The hues appear at full strength and flat, with no tints, steps, gradients or hover variants, in these places. The one exception is the website's shader objects, each the material of one object and never a section's ground: the threads she holds, a fan of lines in her stripes from her orb to her colleagues, on black; Alveare's hive, cells that light in its hues as the work passes; and the company card's holographic foil ("Company card"). Stripes and cells stay pure bands of their hues, never a colour between two of them. Ruled by the founder: "shaders are not backgrounds all over the place".
564
564
 
@@ -568,7 +568,7 @@ The hues appear at full strength and flat, with no tints, steps, gradients or ho
568
568
  - **Agents.** The orbs ("Agent orbs").
569
569
  - **Alveare's channels.** Each channel's mark sits on a tile of one hue, in the Mac's sidebar and the iPhone's channel list; the channel's id picks the hue, so a channel is the same colour on every device, and a direct conversation shows its people instead. On the iPhone the person's own messages are cobalto bubbles with white text. Ruled by the owner.
570
570
  - **Objects.** The saturated pieces of the art direction's objects, one per benefit ("Iconography").
571
- - **The logo.** The dot of the wordmark, the icon tile and Alveare's striped mark ("Brand media authority"), and the compact company identity marks ("Identity marks").
571
+ - **The logo.** The dot of the wordmark, the icon tile and Alveare's and Alveare Server's marks ("Brand media authority"), and the compact company identity marks ("Identity marks").
572
572
  - **Imagery.** Art direction reads these tokens and keeps no private palette.
573
573
 
574
574
  Text on a colour ground is the pole that clears AA on that hue: `{colors.identity-white}` on cobalto and rosso; `{colors.identity-ink}` on verde, arancio and giallo. A control on a colour ground uses the button's on-colour variants ("Components"). Product chrome in the Portal and Nadia stays neutral: grounds, text, lines and navigation, a selected row included, whatever it opens. Colour inside that structure is the identity palette at full strength in both themes: the primary control and the on switch in cobalto, feedback and state in cobalto, verde, giallo and rosso with their pole for text, and links in cobalto, the dark theme's links and coloured text one light step of the same hue. It also identifies Agents, providers and categories; an Agent's own colour is her orb, so her row in a list never takes a hue. Choose a typed component tone from the semantic palette; never paint a control with an arbitrary identity hex.
@@ -1215,7 +1215,7 @@ The company is **nadicode**, lowercase. Its logo is the n, and the name drawn in
1215
1215
  3. **Icon.** The mark on a tile with rounded corners, in one of three fields. The brand tile, the white n on `{colors.identity-cobalto}`, is for favicons, avatars and the website's app icon, and is the one place the brand colour fills a logo's field. Where the platform crops the picture itself, a profile picture cut to a circle or a square and a phone's home screen, the tile has square corners and the platform draws its shape: the avatar is the brand tile, or the light or dark tile where a profile wants white or black, with the n across 46% of the side, so a circle cut keeps it whole with its clear space. The mark is never a profile picture: it has no field, and its n runs to the edges a crop cuts. The light tile, the `{colors.ink}` n on white, is a desktop app's icon, which keeps it in the system's dark appearance. The dark tile, the white n on `{colors.identity-ink}`, is the icon wherever else it sits on a dark ground: a dark slide, an app's own dark screen.
1216
1216
  4. **Signature form.** The light tile with its construction guides in `{colors.logo-guide}`: a dotted circle around the arch whose radius is the golden section of the n's width, the two centre axes, and the n's width and height, each measured 1.000. It is drawn from 128 px up, on light grounds: Nadia's desktop icon, a cover or closing slide, a poster, print; on a dark ground the dark tile stands in for it. Below 128 px a guide is a fraction of a pixel, so the icon drops them; drawn on a screen as a logo, such as at the top of an entry scene, it drops them at any size.
1217
1217
 
1218
- Alveare's mark is its own, chosen by the founder: the AV line, nadicode's n arch running down into a V's cup, drawn as a ribbon of six flat stripes 7 units wide, from the outside of the arch in: cobalto in its dark step (`{colors.dark-logo-dot}`), arancio, verde, rosso, giallo, azzurro. Six lanes running as one line are the hive. It sits on the dark tile everywhere, the app icon included, since Alveare opens onto black, and on a black screen it stands alone without its tile. Alveare Server's mark is the same ribbon turned and mirrored into an S. Both are drawn in `apps/alveare/brand/icons/`.
1218
+ Alveare's mark is its own, chosen by the founder: six flat honeycomb cells, pointed at the top, stacked one, two and three into a pyramid, in the identity colours row by row from the top: cobalto in its dark step (`{colors.dark-logo-dot}`), arancio, verde, rosso, giallo, azzurro. Six cells of one comb are the hive. It sits on the dark tile everywhere, the app icon included, since Alveare opens onto black, and on a black screen it stands alone without its tile. Alveare Server's mark is a server rack of six bands, each with its light, in the same colours in the same order from the top, drawn a tenth smaller than the cells so the solid rack weighs what the open cells do beside it. Both are drawn from one geometry source, `@nadicodeai/design-system/assets/alveare-geometry`: Alveare's app icons and marks and `PlatformMark` render it, and nothing copies its paths.
1219
1219
 
1220
1220
  The mark and the wordmark never stand side by side: the wordmark already begins with the n, so together they read "n nadicode". Each place takes one of them.
1221
1221
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/design-system",
3
- "version": "12.1.180",
3
+ "version": "12.1.182",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -206,6 +206,11 @@
206
206
  "import": "./src/assets/logo-geometry.js",
207
207
  "default": "./src/assets/logo-geometry.js"
208
208
  },
209
+ "./assets/alveare-geometry": {
210
+ "types": "./src/assets/alveare-geometry.d.ts",
211
+ "import": "./src/assets/alveare-geometry.js",
212
+ "default": "./src/assets/alveare-geometry.js"
213
+ },
209
214
  "./assets/house-icons": {
210
215
  "types": "./src/assets/house-icons.d.ts",
211
216
  "import": "./src/assets/house-icons.js",
@@ -223,6 +228,7 @@
223
228
  "LICENSES",
224
229
  "src/assets/*.d.ts",
225
230
  "src/assets/logo-geometry.js",
231
+ "src/assets/alveare-geometry.js",
226
232
  "src/assets/house-icons.js",
227
233
  "src/assets/empty",
228
234
  "src/tokens",
@@ -0,0 +1,22 @@
1
+ export type AlveareToken =
2
+ | "dark-logo-dot"
3
+ | "identity-arancio"
4
+ | "identity-verde"
5
+ | "identity-rosso"
6
+ | "identity-giallo"
7
+ | "identity-azzurro";
8
+
9
+ export type AlveareGeometry = Readonly<{
10
+ viewBox: string;
11
+ nodes: readonly Readonly<{
12
+ element: "path";
13
+ attributes: Readonly<Record<string, string>>;
14
+ }>[];
15
+ }>;
16
+
17
+ /** The colour a consumer paints the shape that takes `token` with. */
18
+ export type AlvearePaint = (token: AlveareToken) => string;
19
+
20
+ export declare const ALVEARE_DRAWING: number;
21
+ export declare function alveareMarkGeometry(paint: AlvearePaint): AlveareGeometry;
22
+ export declare function alveareServerMarkGeometry(paint: AlvearePaint): AlveareGeometry;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Framework-neutral SVG geometry for Alveare's and Alveare Server's marks.
3
+ *
4
+ * This module is the only drawing of either mark. Alveare's app icons, the marks
5
+ * its scenes and the website show, and the shared PlatformMark render these
6
+ * descriptors instead of copying paths.
7
+ *
8
+ * Both marks are drawn in one square of ALVEARE_DRAWING units, centred in it,
9
+ * so an icon places them alike; each view box crops to its own shapes. A
10
+ * consumer paints each shape by the design token it takes, which `paint`
11
+ * turns into a colour: a CSS variable in a page, a hex value in a file. The
12
+ * six tokens run from the top in the same order on both marks.
13
+ *
14
+ * Alveare's mark is six pointy-top cells of one comb, stacked one, two and
15
+ * three: on a grid of radius 31, each cell is 5 units from its neighbours.
16
+ * Alveare Server's is a rack 160 units square of six bands 6 apart, its outer
17
+ * corners of radius 18, each band with a light of radius 4.4 cut out 50 units
18
+ * right of centre; it is drawn at nine tenths, so the solid rack weighs what
19
+ * the open cells do beside it.
20
+ */
21
+
22
+ const freeze = (geometry) => {
23
+ geometry.nodes.forEach((node) => {
24
+ Object.freeze(node.attributes);
25
+ Object.freeze(node);
26
+ });
27
+ Object.freeze(geometry.nodes);
28
+ return Object.freeze(geometry);
29
+ };
30
+
31
+ const round2 = (value) => Number(value.toFixed(2)).toString();
32
+
33
+ export const ALVEARE_DRAWING = 200;
34
+ const CENTRE = ALVEARE_DRAWING / 2;
35
+
36
+ const TOKENS = [
37
+ "dark-logo-dot",
38
+ "identity-arancio",
39
+ "identity-verde",
40
+ "identity-rosso",
41
+ "identity-giallo",
42
+ "identity-azzurro",
43
+ ];
44
+
45
+ const viewBox = (left, top, right, bottom) =>
46
+ [left, top, right - left, bottom - top].map(round2).join(" ");
47
+
48
+ const GRID = 31;
49
+ const CELL_GAP = 5;
50
+ const CELL_RADIUS = GRID - CELL_GAP / Math.sqrt(3);
51
+ /** Each cell's place on the grid, in cells across and rows down from the centre. */
52
+ const CELLS = [
53
+ [0, -1],
54
+ [-0.5, 0],
55
+ [0.5, 0],
56
+ [-1, 1],
57
+ [0, 1],
58
+ [1, 1],
59
+ ];
60
+
61
+ const cellCorners = ([across, down]) => {
62
+ const x = CENTRE + across * Math.sqrt(3) * GRID;
63
+ const y = CENTRE + down * 1.5 * GRID;
64
+ return Array.from({ length: 6 }, (_, corner) => {
65
+ const angle = ((-90 + 60 * corner) * Math.PI) / 180;
66
+ return [
67
+ Number(round2(x + CELL_RADIUS * Math.cos(angle))),
68
+ Number(round2(y + CELL_RADIUS * Math.sin(angle))),
69
+ ];
70
+ });
71
+ };
72
+
73
+ /** Alveare's mark: the six cells, painted from the top. */
74
+ export const alveareMarkGeometry = (paint) => {
75
+ const cells = CELLS.map(cellCorners);
76
+ const xs = cells.flat().map(([x]) => x);
77
+ const ys = cells.flat().map(([, y]) => y);
78
+ return freeze({
79
+ viewBox: viewBox(Math.min(...xs), Math.min(...ys), Math.max(...xs), Math.max(...ys)),
80
+ nodes: cells.map((corners, index) => ({
81
+ element: "path",
82
+ attributes: {
83
+ fill: paint(TOKENS[index]),
84
+ d: `M${corners.map(([x, y]) => `${x} ${y}`).join("L")}Z`,
85
+ },
86
+ })),
87
+ });
88
+ };
89
+
90
+ const RACK_BALANCE = 0.9;
91
+ const RACK_SIDE = 160;
92
+ const BAND_GAP = 6;
93
+ const CORNER = 18;
94
+ const LIGHT = 4.4;
95
+ const LIGHT_RIGHT = 50;
96
+
97
+ /** Alveare Server's mark: the rack's six bands, painted from the top, each with its light. */
98
+ export const alveareServerMarkGeometry = (paint) => {
99
+ const at = (unit) => Number(round2(CENTRE + RACK_BALANCE * (unit - CENTRE)));
100
+ const size = (unit) => round2(RACK_BALANCE * unit);
101
+ const left = CENTRE - RACK_SIDE / 2;
102
+ const right = CENTRE + RACK_SIDE / 2;
103
+ const band = (RACK_SIDE - 5 * BAND_GAP) / 6;
104
+ const r = size(CORNER);
105
+ const corner = `A${r} ${r} 0 0 1`;
106
+ const light = size(LIGHT);
107
+ const lightX = CENTRE + LIGHT_RIGHT;
108
+ return freeze({
109
+ viewBox: viewBox(at(left), at(left), at(right), at(right)),
110
+ nodes: TOKENS.map((token, index) => {
111
+ const top = left + index * (band + BAND_GAP);
112
+ const bottom = top + band;
113
+ const middle = at(top + band / 2);
114
+ let body = `M${at(left)} ${at(top)}H${at(right)}V${at(bottom)}H${at(left)}Z`;
115
+ if (index === 0) {
116
+ body = `M${at(left)} ${at(bottom)}V${at(top + CORNER)}${corner} ${at(left + CORNER)} ${at(top)}H${at(right - CORNER)}${corner} ${at(right)} ${at(top + CORNER)}V${at(bottom)}Z`;
117
+ } else if (index === TOKENS.length - 1) {
118
+ body = `M${at(left)} ${at(top)}H${at(right)}V${at(bottom - CORNER)}${corner} ${at(right - CORNER)} ${at(bottom)}H${at(left + CORNER)}${corner} ${at(left)} ${at(bottom - CORNER)}Z`;
119
+ }
120
+ const hole = `M${at(lightX + LIGHT)} ${middle}A${light} ${light} 0 1 0 ${at(lightX - LIGHT)} ${middle}A${light} ${light} 0 1 0 ${at(lightX + LIGHT)} ${middle}Z`;
121
+ return {
122
+ element: "path",
123
+ attributes: { fill: paint(token), "fill-rule": "evenodd", d: `${body} ${hole}` },
124
+ };
125
+ }),
126
+ });
127
+ };