@carissa-io/theme-default 0.0.0-stage → 0.1.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.
- package/DESIGN.md +174 -0
- package/README.md +45 -2
- package/dist/index.css +1148 -0
- package/package.json +131 -4
- package/scss/index.scss +511 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Carissa Default theme
|
|
2
|
+
|
|
3
|
+
**Swiss precision for high-performance interfaces.** International Typographic Style: information over decoration.
|
|
4
|
+
|
|
5
|
+
This is the design document of the **Carissa Default theme** (`@carissa-io/theme-default`): the decisions behind its
|
|
6
|
+
values. The components are style-agnostic; this theme gives them their look through SCSS variable overrides in
|
|
7
|
+
[scss/index.scss](scss/index.scss), and its palette and shared values (radius, focus ring, transition...) are Sass
|
|
8
|
+
variables at the top of that file. Other themes (tech terminal, brutalist, ...) are their own `theme-<name>` repositories,
|
|
9
|
+
never changes to the components.
|
|
10
|
+
|
|
11
|
+
## Principles
|
|
12
|
+
- **Objective clarity:** information is prioritized over decoration.
|
|
13
|
+
- **Mathematical grid:** precise alignment and spacing, square corners.
|
|
14
|
+
- **High contrast:** absolute black (`#000`) for primary actions and focus; neutral scales for meta-data.
|
|
15
|
+
|
|
16
|
+
## Palette
|
|
17
|
+
| Role | Value |
|
|
18
|
+
| :--- | :--- |
|
|
19
|
+
| Primary | `#000000` |
|
|
20
|
+
| Surface | `#fafafa` / `#f5f5f5` |
|
|
21
|
+
| Border (fields) | `#737373` (hover and focus: `#000`, focus adds a 1px ring) |
|
|
22
|
+
| Text (body) | `#171717` |
|
|
23
|
+
| Text (meta, placeholder) | `#737373` (4.7:1; lighter greys only for disabled states) |
|
|
24
|
+
|
|
25
|
+
## Typography
|
|
26
|
+
| Variant | Purpose | Style |
|
|
27
|
+
| :--- | :--- | :--- |
|
|
28
|
+
| `h1` | Primary page header | Black (900), tight tracking |
|
|
29
|
+
| `h2` | Section header | Black (900) |
|
|
30
|
+
| `h3` | Sub-section | Bold (700) |
|
|
31
|
+
| `body` | Main content | Regular (400) |
|
|
32
|
+
| `label` | Metadata / tags / field labels | Bold (700), uppercase, widest tracking |
|
|
33
|
+
| `mono` | System data | Technical precision |
|
|
34
|
+
|
|
35
|
+
Font family: a neutral grotesque stack, `Helvetica Neue, Helvetica, Arial, Liberation Sans, sans-serif`, in keeping with
|
|
36
|
+
the International Typographic Style. No webfont is loaded, so a theme never depends on the network. Field text is
|
|
37
|
+
1rem (16px, which also avoids the zoom-on-focus of mobile browsers); labels are 11px.
|
|
38
|
+
|
|
39
|
+
## Buttons
|
|
40
|
+
Three variants from [components-button](components/components-button), all square, 44px tall, with the same uppercase,
|
|
41
|
+
bold, tracked 11px label as the field labels and 200ms color transitions.
|
|
42
|
+
- **Primary:** black background, white text; hover `#262626`, active `#404040`.
|
|
43
|
+
- **Secondary:** white background, 1px `#737373` border; the border turns black on hover.
|
|
44
|
+
- **Ghost:** transparent; a `#f5f5f5` surface appears on hover.
|
|
45
|
+
- **Disabled:** `#a3a3a3` text on a `#f5f5f5` surface (primary), or a lighter border (secondary, ghost); no hover.
|
|
46
|
+
- **Focus:** keyboard focus (`:focus-visible`) follows the general rule below: secondary and ghost thicken their border to
|
|
47
|
+
2px; primary (filled) draws a 1px black ring offset by 2px.
|
|
48
|
+
|
|
49
|
+
## Checkbox
|
|
50
|
+
From [components-checkbox](components/components-checkbox): the whole row (box and label) is the click target and is at
|
|
51
|
+
least 44px tall. The label is body text (1rem, `#171717`), not the uppercase field-label style.
|
|
52
|
+
- **Box:** 20px square, square corners, 1px `#737373` border; the border turns black on hover.
|
|
53
|
+
- **Checked:** black background with a white Lucide `check` mark.
|
|
54
|
+
- **Disabled:** `#a3a3a3` label and mark on a `#f5f5f5` box with a `#d4d4d4` border; no hover.
|
|
55
|
+
- **Error:** `#dc3545`, 13px, under the row; the box border turns red too. The built-in error shows after the first
|
|
56
|
+
interaction, an error passed by the parent shows at once.
|
|
57
|
+
- **Focus:** keyboard focus (`:focus-visible`): an empty box thickens its border to 2px; a checked (black) box draws a 1px
|
|
58
|
+
black ring offset by 2px.
|
|
59
|
+
|
|
60
|
+
## Switch
|
|
61
|
+
From [components-switch](components/components-switch), a checkbox with `role="switch"`, same row, label and error.
|
|
62
|
+
- **Track:** 40 x 20px, square, white with a 1px `#737373` border (black on hover); a `#737373` square thumb.
|
|
63
|
+
- **On:** black track, white thumb; the thumb slides in 200ms, like the other color transitions.
|
|
64
|
+
- **Disabled:** `#f5f5f5` track with a `#d4d4d4` border and an `#a3a3a3` thumb.
|
|
65
|
+
- **Focus:** an off track thickens its border to 2px; an on (black) track draws a 1px black ring offset by 2px.
|
|
66
|
+
|
|
67
|
+
## Radio group
|
|
68
|
+
From [components-radio-group](components/components-radio-group): a fieldset whose legend uses the field-label style
|
|
69
|
+
(11px, bold, uppercase, tracked, `#737373`). Each option row (dot and label) is the click target and is at least 44px
|
|
70
|
+
tall; the label is body text (1rem, `#171717`).
|
|
71
|
+
- **Dot:** 20px circle (the one round shape in the theme: a radio is a circle, so its radius is not the square `$radius`),
|
|
72
|
+
1px `#737373` border; the border turns black on hover.
|
|
73
|
+
- **Checked:** black border with a 10px black inner dot on white.
|
|
74
|
+
- **Disabled:** `#a3a3a3` label on a `#f5f5f5` dot with a `#d4d4d4` border; no hover. A single option can be disabled too.
|
|
75
|
+
- **Error:** `#dc3545`, 13px, under the options; the dots get a red border. The built-in error shows after the first
|
|
76
|
+
interaction (a change, or focus leaving the group), an error passed by the parent shows at once.
|
|
77
|
+
- **Focus:** keyboard focus (`:focus-visible`) thickens the dot's border from 1px to 2px, black, with no separate ring (so a
|
|
78
|
+
checked radio never reads as a double circle); the arrow keys move the selection.
|
|
79
|
+
|
|
80
|
+
## Textarea
|
|
81
|
+
From [components-textarea](components/components-textarea), which is the input with `multiline`: it uses exactly the
|
|
82
|
+
input's look (notched uppercase label, 1px `#737373` border that turns black on hover and focus, 1px black focus ring,
|
|
83
|
+
`#dc3545` error). Only the control differs:
|
|
84
|
+
- **Height:** at least 6rem (about three lines of 16px text) and resizable vertically only, so it never breaks the grid;
|
|
85
|
+
`rows` sets the initial height.
|
|
86
|
+
- **Disabled and error:** same as the input (`#a3a3a3` on `#f5f5f5`; red border and 13px message).
|
|
87
|
+
|
|
88
|
+
## Slider
|
|
89
|
+
From [components-slider](components/components-slider), a native range input; the row is at least 44px tall. The label
|
|
90
|
+
uses the field-label style (11px, bold, uppercase, tracked, `#737373`); the optional live value, on the same line at
|
|
91
|
+
the right, is body text (1rem, `#171717`).
|
|
92
|
+
- **Track:** a 2px `#737373` line, square; the part before the thumb is black.
|
|
93
|
+
- **Thumb:** 20px black square with a 1px white border (so it stays distinct where it crosses the track); `#262626` on
|
|
94
|
+
hover, `#404040` while dragging.
|
|
95
|
+
- **Disabled:** `#a3a3a3` label, thumb and fill on a `#d4d4d4` track; no hover.
|
|
96
|
+
- **Focus:** keyboard focus (`:focus-visible`) draws a 1px black ring offset by 2px around the thumb.
|
|
97
|
+
|
|
98
|
+
## Popover
|
|
99
|
+
From [components-popover](components/components-popover), the floating panel behind the select, combobox and date picker:
|
|
100
|
+
white, a 1px `#737373` border, square corners and no shadow (flat, like the rest of the theme), 4px below its anchor.
|
|
101
|
+
It fades in over 200ms and opens above the anchor when there is no room below. It has no padding of its own: the
|
|
102
|
+
content (a list, a calendar) brings its spacing.
|
|
103
|
+
|
|
104
|
+
## Listbox
|
|
105
|
+
From [components-listbox](components/components-listbox), the option list used inside the popover. Each option is at
|
|
106
|
+
least 44px tall, body text (1rem, `#171717`) with 0.75rem of horizontal padding.
|
|
107
|
+
- **Active** (hover or keyboard cursor): `#f5f5f5` surface.
|
|
108
|
+
- **Selected:** bold text with a 2px black bar on the inline-start edge.
|
|
109
|
+
- **Disabled:** `#a3a3a3`, no hover.
|
|
110
|
+
- **Empty:** a `#737373` message with 0.75rem of padding.
|
|
111
|
+
- **Height:** at most 15rem; longer lists scroll.
|
|
112
|
+
|
|
113
|
+
## Select
|
|
114
|
+
From [components-select](components/components-select): a custom list, so it looks the same in every browser. The closed
|
|
115
|
+
field is the Input (notched uppercase label, 1px `#737373` border, black on hover and focus, 44px tall); the value reads
|
|
116
|
+
as body text, and the placeholder is `#737373` like the input placeholder. A 16px `#737373` chevron sits on the right
|
|
117
|
+
and turns black; when the list is open it rotates 180 degrees over 200ms and the border stays black. The open panel is
|
|
118
|
+
the Popover and the options are the Listbox, as wide as the field. Errors, required marker and disabled look are the
|
|
119
|
+
Input's.
|
|
120
|
+
|
|
121
|
+
## Combobox
|
|
122
|
+
From [components-combobox](components/components-combobox): the Select field with a text input in place of the button, so
|
|
123
|
+
the user can type to filter the list. Same Input look (notched uppercase label, 1px `#737373` border, black on hover and
|
|
124
|
+
focus, 44px tall), same 16px `#737373` chevron that turns black and rotates 180 degrees when the list is open. The text
|
|
125
|
+
is body text (`#171717`) and the placeholder `#737373`. A press anywhere in the box focuses the input (the chevron is
|
|
126
|
+
decorative, not a button); the list opens when typing or with the arrow keys. The panel is the Popover and the options
|
|
127
|
+
the Listbox, as wide as the field; a message such as "No results" uses the Listbox empty style. Errors, required marker
|
|
128
|
+
and disabled look are the Input's.
|
|
129
|
+
|
|
130
|
+
## Upload
|
|
131
|
+
From [components-upload](components/components-upload): the Input field turned into a dropzone. The box keeps the
|
|
132
|
+
notched uppercase label and the 1px `#737373` border but dashed, black on hover; while files are dragged over it the
|
|
133
|
+
border is black and the background `#f5f5f5`. The zone is at least 5.5rem tall and centers its content as muted (`#737373`)
|
|
134
|
+
body text, and the whole box is the click target. Chosen files are listed below the zone as 44px rows split by a 1px
|
|
135
|
+
`#e5e5e5` line: the name in body text with an ellipsis, and a 44px square remove button (a multiplication sign, `#737373`,
|
|
136
|
+
black on hover, 1px black focus ring). The focus ring of the box is the Input's. Errors, required marker and disabled look
|
|
137
|
+
(`#a3a3a3` on `#f5f5f5`) are the Input's.
|
|
138
|
+
|
|
139
|
+
## Calendar
|
|
140
|
+
From [components-calendar](components/components-calendar): a standalone month grid (the date picker shows it inside the
|
|
141
|
+
Popover, which draws the only border). It has no outer border. The header is two 44px navigation buttons (muted
|
|
142
|
+
chevrons that turn black, 1px black focus ring drawn inside) around the month and year in the label style (11px, bold,
|
|
143
|
+
uppercase, `0.1em` tracking, `#737373`); the weekday row uses the same style. Days are 44px squares in body text
|
|
144
|
+
(`#171717`) with square corners and a `#f5f5f5` hover. The selected day is a black square with white text; today is a
|
|
145
|
+
1px black border, drawn inside the square so nothing shifts. Days outside `min..max` are `#a3a3a3` with no hover, and
|
|
146
|
+
the days of the neighbor months are left empty. The keyboard focus ring is 1px black, inset. The grid always shows six
|
|
147
|
+
weeks so the height never changes between months.
|
|
148
|
+
|
|
149
|
+
## Date picker
|
|
150
|
+
From [components-date-picker](components/components-date-picker): the Select field with a calendar in the panel. The closed
|
|
151
|
+
field is the Input (notched uppercase label, 1px `#737373` border, black on hover and focus, 44px tall); the chosen date
|
|
152
|
+
reads as body text (for example "Oct 14, 2026") and the placeholder is `#737373`. A 16px `#737373` calendar icon sits on the
|
|
153
|
+
right and turns black on hover and while open; it does not rotate, and the border stays black while open. The open panel
|
|
154
|
+
is the Popover (1px `#737373` border, no shadow) with 0.5rem of padding around the Calendar, as wide as the calendar
|
|
155
|
+
needs, not as the field. Errors, required marker and disabled look are the Input's.
|
|
156
|
+
|
|
157
|
+
## Search
|
|
158
|
+
From [components-search](components/components-search): the Input of type search with two inline parts built on the input's
|
|
159
|
+
`start` and `end` slots. The field is the Input's (notched uppercase label, 1px `#737373` border, black on hover and focus,
|
|
160
|
+
44px tall; the keyboard focus rule below applies). A 20px `#737373` magnifier icon sits on the left and turns black while the
|
|
161
|
+
field has the focus. The clear button appears on the right only while there is text: a 16px `#737373` cross in the Input's
|
|
162
|
+
44px side-button box that turns black on hover; the browser's own clear button is hidden so the look is the same everywhere.
|
|
163
|
+
Errors, required marker and disabled look are the Input's. The Input and Textarea accept `start` and `end` content (a prefix
|
|
164
|
+
icon, a unit) in the same muted color (`#737373`) with `0.5rem` between the content and the text.
|
|
165
|
+
|
|
166
|
+
## Keyboard focus (all controls)
|
|
167
|
+
One rule keeps the controls coherent: **an outlined control thickens its own border from 1px to 2px, black** (text fields,
|
|
168
|
+
select, combobox, date picker, textarea, upload, empty checkbox, off switch, radio, secondary and ghost buttons); **a filled
|
|
169
|
+
(black) control draws a separate 1px black ring offset by 2px**, because a thicker border would be invisible on a black fill
|
|
170
|
+
(primary button, checked checkbox, on switch, slider thumb). Calendar days and the upload remove button use an inset 1px ring.
|
|
171
|
+
|
|
172
|
+
## Guidelines
|
|
173
|
+
- **Accessibility:** 4.5:1 contrast for body text; interactive elements are at least 44px tall.
|
|
174
|
+
- **Motion:** subtle, 200-300ms. Hover gives immediate feedback (border or color shift).
|
package/README.md
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @carissa-io/theme-default
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Carissa Default theme: **Swiss minimalism** (square corners, absolute black for primary actions and focus, a neutral
|
|
4
|
+
grey scale, a neutral grotesque type stack). The Carissa components ship neutral defaults and no look of their own; this
|
|
5
|
+
package gives them one by overriding their SCSS variables. The decisions behind the values are in [DESIGN.md](DESIGN.md).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
```bash
|
|
9
|
+
npm install @carissa-io/theme-default
|
|
10
|
+
```
|
|
11
|
+
The compiled CSS needs nothing else. To compile the SCSS yourself, also install the components you use (they are optional
|
|
12
|
+
peer dependencies).
|
|
13
|
+
|
|
14
|
+
## Use it
|
|
15
|
+
1. **Compiled CSS** (nothing to compile; the components must still be installed to render):
|
|
16
|
+
```js
|
|
17
|
+
import '@carissa-io/theme-default/css'
|
|
18
|
+
```
|
|
19
|
+
It is also the package's main export, so `import '@carissa-io/theme-default'` works with a bundler that handles CSS.
|
|
20
|
+
2. **SCSS source:** forward or `@use` it from your own Sass entry, with `@carissa-io/*` resolvable (`node_modules` on the
|
|
21
|
+
load path):
|
|
22
|
+
```scss
|
|
23
|
+
@use '@carissa-io/theme-default/scss';
|
|
24
|
+
```
|
|
25
|
+
3. **A starting point for a new theme:** copy `scss/index.scss` and change the values. In the Carissa workspace,
|
|
26
|
+
`scripts/new-theme.sh <name> "<description>"` does this and sets up the repo.
|
|
27
|
+
|
|
28
|
+
## What it configures
|
|
29
|
+
`components-input`, `-button`, `-checkbox`, `-switch`, `-radio-group`, `-slider`, `-popover`, `-listbox`, `-select`,
|
|
30
|
+
`-combobox`, `-upload`, `-calendar`, `-date-picker` and `-search`. `components-email`, `-password`, `-url` and `-textarea`
|
|
31
|
+
are styled through the input.
|
|
32
|
+
|
|
33
|
+
## Change a value
|
|
34
|
+
The palette and the values shared by every component (radius, minimum height, focus ring, transition, label style, dropdown
|
|
35
|
+
arrow) are Sass variables at the top of `scss/index.scss`; each component has one `@use ... with (...)` block of overrides
|
|
36
|
+
below. Change the value, run `npm run build`, and update [DESIGN.md](DESIGN.md) if the decision changes.
|
|
37
|
+
|
|
38
|
+
## Develop
|
|
39
|
+
```bash
|
|
40
|
+
npm install
|
|
41
|
+
npm run lint # compile, fail on any Sass error or warning
|
|
42
|
+
npm test # builds, then checks every component root has rules and dist/ is current
|
|
43
|
+
npm run build # dist/index.css
|
|
44
|
+
```
|
|
45
|
+
`dist/` is not committed; it is built before every publish. The package ships `dist`, `scss` and `DESIGN.md`. Releases
|
|
46
|
+
follow the Carissa project rules: branch and pull request, CI publishes when the version on `main` is new.
|