@adrienlcp/styles 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adrien Lacourpaille
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # @adrienlcp/styles
2
+
3
+ The styling floor every app of mine starts from: a reset, a reduced-motion
4
+ switch, and the Sass mixins that CSS cannot write yet. Plain `.css` for what
5
+ CSS can say, `.sass` only for what it cannot — a custom property is not allowed
6
+ in a media query or in `unicode-range`, and CSS has no mixins.
7
+
8
+ ```bash
9
+ pnpm add @adrienlcp/styles
10
+ ```
11
+
12
+ ## Plain CSS
13
+
14
+ | File | Does |
15
+ | --- | --- |
16
+ | `reset.css` | Box sizing, zeroed margins and paddings, inherited fonts on controls, bare buttons and lists, balanced headings, pretty paragraphs. Inside `@layer reset` |
17
+ | `reduced-motion.css` | Collapses `--transition-fast`, `--transition-base` and `--transition-slow` to `0ms` under `prefers-reduced-motion: reduce`. Unlayered, so it beats the tokens wherever they are defined |
18
+
19
+ Import them once — from JavaScript, or from the global stylesheet in Sass:
20
+
21
+ ```sass
22
+ @use '@adrienlcp/styles/reset.css'
23
+ @use '@adrienlcp/styles/reduced-motion.css'
24
+ ```
25
+
26
+ `reset.css` declares a layer: set the order in the document head before any
27
+ stylesheet loads, so the reset stays under every component rule —
28
+ `<style>@layer reset, tokens, base, components;</style>`.
29
+
30
+ ## Sass
31
+
32
+ Resolved through the `sass` export condition, which Vite reads; with the Sass
33
+ CLI, use `pkg:@adrienlcp/styles/focus` and `--pkg-importer=node`.
34
+
35
+ ### `breakpoints`
36
+
37
+ ```sass
38
+ @use '@adrienlcp/styles/breakpoints'
39
+
40
+ .page
41
+ @include breakpoints.wide
42
+ grid-template-columns: 1fr 1fr
43
+ ```
44
+
45
+ `$wide-screen` is `900px`; `wide` is `width >= $wide-screen` and `narrow` its
46
+ exact complement. Another value: `@use '@adrienlcp/styles/breakpoints' with
47
+ ($wide-screen: 1024px)`.
48
+
49
+ ### `focus`
50
+
51
+ `ring` and `ring-inset` draw the outline on `[data-focus-visible]` (react-aria)
52
+ and `:focus-visible` (a native element). `hovered` wraps a hover style in
53
+ `(hover: hover) and (pointer: fine)`, since a touch screen replays hover after a
54
+ tap and leaves it stuck; `$except` skips a state.
55
+
56
+ ```sass
57
+ @use '@adrienlcp/styles/focus'
58
+
59
+ .button
60
+ @include focus.ring
61
+
62
+ @include focus.hovered($except: '[data-disabled]')
63
+ background: var(--hover)
64
+ ```
65
+
66
+ Configure the ring once, in the app's own `_focus.sass`, and have components
67
+ `@use` that file:
68
+
69
+ ```sass
70
+ @forward '@adrienlcp/styles/focus' with ($ring-color: var(--focus), $ring-offset: 3px, $ring-width: 3px)
71
+ ```
72
+
73
+ ### `fonts`
74
+
75
+ `$latin` and `$latin-ext` are the unicode ranges Google Fonts cuts a Latin face
76
+ into; `font-face` declares one self-hosted `woff2` file.
77
+
78
+ ```sass
79
+ @use '@adrienlcp/styles/fonts'
80
+
81
+ @include fonts.font-face('Archivo', '/fonts/archivo-latin.woff2', fonts.$latin, $weight: 100 900, $stretch: 62% 125%)
82
+ @include fonts.font-face('Archivo', '/fonts/archivo-latin-ext.woff2', fonts.$latin-ext, $weight: 100 900, $stretch: 62% 125%)
83
+ ```
84
+
85
+ `$weight`, `$style` (`normal`), `$stretch` (left out) and `$display` (`swap`)
86
+ are optional.
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "author": "Adrien Lacourpaille",
3
+ "bugs": "https://github.com/AdrienLcp/packages/issues",
4
+ "description": "A reset, a reduced-motion switch, and Sass mixins for self-hosted fonts, a breakpoint, focus rings and pointer-only hover",
5
+ "devDependencies": {
6
+ "sass": "^1.105.0"
7
+ },
8
+ "exports": {
9
+ "./breakpoints": {
10
+ "sass": "./src/_breakpoints.sass"
11
+ },
12
+ "./focus": {
13
+ "sass": "./src/_focus.sass"
14
+ },
15
+ "./fonts": {
16
+ "sass": "./src/_fonts.sass"
17
+ },
18
+ "./package.json": "./package.json",
19
+ "./reduced-motion.css": "./src/reduced-motion.css",
20
+ "./reset.css": "./src/reset.css"
21
+ },
22
+ "files": [
23
+ "src/*.css",
24
+ "src/*.sass"
25
+ ],
26
+ "homepage": "https://github.com/AdrienLcp/packages/tree/main/packages/styles#readme",
27
+ "keywords": [
28
+ "css",
29
+ "focus-visible",
30
+ "font-face",
31
+ "reset",
32
+ "sass"
33
+ ],
34
+ "license": "MIT",
35
+ "name": "@adrienlcp/styles",
36
+ "peerDependencies": {
37
+ "sass": ">=1.80.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "sass": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "repository": {
45
+ "directory": "packages/styles",
46
+ "type": "git",
47
+ "url": "git+https://github.com/AdrienLcp/packages.git"
48
+ },
49
+ "scripts": {
50
+ "test": "vitest run",
51
+ "test:watch": "vitest --watch"
52
+ },
53
+ "sideEffects": [
54
+ "*.css"
55
+ ],
56
+ "type": "module",
57
+ "version": "0.1.0"
58
+ }
@@ -0,0 +1,13 @@
1
+ /// Where a screen stops being a phone's. A media query cannot read a custom
2
+ /// property, so the breakpoint is a Sass value; override it with
3
+ /// `@use '@adrienlcp/styles/breakpoints' with ($wide-screen: 1024px)`.
4
+ $wide-screen: 900px !default
5
+
6
+ /// `wide` and `narrow` are exact complements: every width is one or the other.
7
+ @mixin wide
8
+ @media (width >= $wide-screen)
9
+ @content
10
+
11
+ @mixin narrow
12
+ @media (width < $wide-screen)
13
+ @content
@@ -0,0 +1,32 @@
1
+ /// Configure once, in the app's own `_focus.sass`:
2
+ /// `@forward '@adrienlcp/styles/focus' with ($ring-color: var(--focus) !default)`.
3
+ $ring-width: 2px !default
4
+ $ring-color: currentColor !default
5
+ $ring-offset: 3px !default
6
+
7
+ /// react-aria stamps `data-focus-visible` on keyboard focus only; the
8
+ /// `:focus-visible` twin covers a native element.
9
+ @mixin ring
10
+ &[data-focus-visible],
11
+ &:focus-visible
12
+ outline: $ring-width solid $ring-color
13
+ outline-offset: $ring-offset
14
+
15
+ /// The ring drawn inside the box, for an element a clipping ancestor would cut.
16
+ @mixin ring-inset
17
+ &[data-focus-visible],
18
+ &:focus-visible
19
+ outline: $ring-width solid $ring-color
20
+ outline-offset: calc(-1 * $ring-width)
21
+
22
+ /// Only a real pointer hovers: a touch screen replays hover after a tap and
23
+ /// leaves it stuck until the next one. `$except` skips a state, as in
24
+ /// `hovered($except: '[data-disabled]')`.
25
+ @mixin hovered($except: null)
26
+ @media (hover: hover) and (pointer: fine)
27
+ @if $except
28
+ &[data-hovered]:not(#{$except})
29
+ @content
30
+ @else
31
+ &[data-hovered]
32
+ @content
@@ -0,0 +1,16 @@
1
+ /// The two subsets Google Fonts cuts a Latin face into: a page in English or
2
+ /// French downloads the first file only.
3
+ $latin: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD
4
+ $latin-ext: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF
5
+
6
+ /// One self-hosted `woff2` file. `$weight` and `$stretch` take a range for a
7
+ /// variable face.
8
+ @mixin font-face($family, $url, $unicode-range, $weight: 400, $style: normal, $stretch: null, $display: swap)
9
+ @font-face
10
+ font-display: $display
11
+ font-family: $family
12
+ font-stretch: $stretch
13
+ font-style: $style
14
+ font-weight: $weight
15
+ src: url($url) format('woff2')
16
+ unicode-range: $unicode-range
@@ -0,0 +1,8 @@
1
+ /* Unlayered, so it beats the token definitions whatever layer or file they sit in. */
2
+ @media (prefers-reduced-motion: reduce) {
3
+ :root {
4
+ --transition-fast: 0ms;
5
+ --transition-base: 0ms;
6
+ --transition-slow: 0ms;
7
+ }
8
+ }
package/src/reset.css ADDED
@@ -0,0 +1,61 @@
1
+ @layer reset {
2
+ *,
3
+ *::before,
4
+ *::after {
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ * {
9
+ padding: 0;
10
+ margin: 0;
11
+ }
12
+
13
+ html {
14
+ -webkit-text-size-adjust: 100%;
15
+ text-size-adjust: 100%;
16
+ }
17
+
18
+ body {
19
+ -webkit-font-smoothing: antialiased;
20
+ line-height: 1.5;
21
+ }
22
+
23
+ img,
24
+ picture,
25
+ svg,
26
+ canvas,
27
+ video {
28
+ display: block;
29
+ max-width: 100%;
30
+ }
31
+
32
+ button,
33
+ input,
34
+ select,
35
+ textarea {
36
+ font: inherit;
37
+ color: inherit;
38
+ }
39
+
40
+ button {
41
+ cursor: pointer;
42
+ background: none;
43
+ border: none;
44
+ }
45
+
46
+ h1,
47
+ h2,
48
+ h3,
49
+ h4 {
50
+ text-wrap: balance;
51
+ }
52
+
53
+ p {
54
+ text-wrap: pretty;
55
+ }
56
+
57
+ ul,
58
+ ol {
59
+ list-style: none;
60
+ }
61
+ }