@anydigital/atomic-bricks 1.0.0-alpha.3 → 1.0.0-alpha.5

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/README.md CHANGED
@@ -48,6 +48,79 @@ body {
48
48
 
49
49
  This is automatically applied when you include the stylesheet.
50
50
 
51
+ ### Typography Enhancements
52
+
53
+ Improves text rendering and readability:
54
+
55
+ ```css
56
+ body {
57
+ hyphens: auto;
58
+ -webkit-font-smoothing: antialiased;
59
+ -moz-osx-font-smoothing: grayscale;
60
+ }
61
+ ```
62
+
63
+ - Automatic hyphenation for better text flow
64
+ - Font smoothing for cleaner text rendering across browsers
65
+
66
+ This is automatically applied when you include the stylesheet.
67
+
68
+ ### Prose Styling
69
+
70
+ The `.prose` class provides enhanced typography for article content and long-form text:
71
+
72
+ **Links:**
73
+ - Custom underline offset and thickness (1px default, 2px on hover)
74
+ - Special handling for links containing `small`, `sup`, or `sub` elements
75
+
76
+ **Headings:**
77
+ - `h1` with `small`, `sup`, or `sub` elements get reduced font size (0.5em) and lighter weight
78
+ - `h2` headings (without classes) get a full-width decorative bar above them
79
+ - `h3` and `h4` headings (without classes) get a decorative gradient bar to the left
80
+
81
+ **Tables:**
82
+ - Tables are displayed as blocks with horizontal scrolling
83
+ - On mobile (max-width: 767px), tables get horizontal padding
84
+ - Table cells have consistent vertical padding
85
+ - Workaround for widening columns using hidden `hr` elements
86
+ - Support for headings in Markdown tables using `big` elements
87
+
88
+ **Blockquotes:**
89
+ - Lighter font weight (300)
90
+ - Adjacent `figcaption` elements are styled with italic text, right alignment, and an em dash prefix
91
+
92
+ **Usage:**
93
+
94
+ ```html
95
+ <article class="prose">
96
+ <h1>Article Title</h1>
97
+ <p>Your content here...</p>
98
+ </article>
99
+ ```
100
+
101
+ This is automatically included when you import the stylesheet.
102
+
103
+ ### Flexbox Layout
104
+
105
+ Sets up a flexible column layout structure:
106
+
107
+ ```css
108
+ body {
109
+ display: flex;
110
+ flex-direction: column;
111
+ }
112
+
113
+ body > main {
114
+ display: flex;
115
+ flex-direction: column;
116
+ flex-grow: 1;
117
+ }
118
+ ```
119
+
120
+ The body becomes a flex container with column direction, and `main` elements automatically grow to fill available space. This is useful for creating sticky footers and full-height layouts.
121
+
122
+ This is automatically applied when you include the stylesheet.
123
+
51
124
  ### Breakout CSS
52
125
 
53
126
  Includes [breakout-css](https://github.com/anydigital/breakout-css) utilities for breaking out images and figures beyond their container width. Use the `.breakout` class to allow elements to extend beyond their parent container:
@@ -60,6 +133,90 @@ Includes [breakout-css](https://github.com/anydigital/breakout-css) utilities fo
60
133
 
61
134
  The breakout utilities support images, pictures, figures, canvas, audio, video, tables, pre, iframe, and other media elements. This is automatically included when you import the stylesheet.
62
135
 
136
+ ## Bricks (Template Components)
137
+
138
+ The package includes reusable Nunjucks template macros in the `src/bricks/` directory. These are useful for common web development patterns.
139
+
140
+ ### Navigation (`_nav.njk`)
141
+
142
+ A navigation macro that renders a list of navigation links with proper accessibility attributes.
143
+
144
+ **Parameters:**
145
+ - `navPages` - Array of navigation page objects with `url` and `title` properties
146
+ - `currentPageUrl` - The URL of the current page (used to set `aria-current="page"`)
147
+
148
+ **Usage:**
149
+
150
+ ```njk
151
+ {% from "bricks/_nav.njk" import render %}
152
+ {{ render(navPages, page.url) }}
153
+ ```
154
+
155
+ **Example:**
156
+
157
+ ```njk
158
+ {% set navPages = [
159
+ { url: '/', title: 'Home' },
160
+ { url: '/about', title: 'About' },
161
+ { url: '/contact', title: 'Contact' }
162
+ ] %}
163
+ {% from "bricks/_nav.njk" import render %}
164
+ {{ render(navPages, '/about') }}
165
+ ```
166
+
167
+ **Output:**
168
+
169
+ ```html
170
+ <nav>
171
+ <a href="/">Home</a>
172
+ <a href="/about" aria-current="page">About</a>
173
+ <a href="/contact">Contact</a>
174
+ </nav>
175
+ ```
176
+
177
+ **Compatibility:** Compatible with [Eleventy Navigation plugin](https://www.11ty.dev/docs/plugins/navigation/#bring-your-own-html-render-the-menu-items-manually).
178
+
179
+ ### Google Tag Manager (`_gtm.njk`)
180
+
181
+ A macro for embedding Google Tag Manager scripts in your pages.
182
+
183
+ **Parameters:**
184
+ - `gtmId` - Your Google Tag Manager container ID (e.g., `GTM-XXXXXXX`)
185
+ - `bodyFallback` - Boolean flag (default: `false`). When `false`, renders the script tag for the `<head>`. When `true`, renders the noscript fallback for the `<body>`.
186
+
187
+ **Usage:**
188
+
189
+ In your base template's `<head>`:
190
+
191
+ ```njk
192
+ {% from "bricks/_gtm.njk" import render %}
193
+ {{ render('GTM-XXXXXXX') }}
194
+ ```
195
+
196
+ In your base template's `<body>` (right after the opening tag):
197
+
198
+ ```njk
199
+ {% from "bricks/_gtm.njk" import render %}
200
+ {{ render('GTM-XXXXXXX', true) }}
201
+ ```
202
+
203
+ **Example:**
204
+
205
+ ```njk
206
+ <!DOCTYPE html>
207
+ <html>
208
+ <head>
209
+ {% from "bricks/_gtm.njk" import render %}
210
+ {{ render('GTM-XXXXXXX') }}
211
+ </head>
212
+ <body>
213
+ {% from "bricks/_gtm.njk" import render %}
214
+ {{ render('GTM-XXXXXXX', true) }}
215
+ <!-- Your content -->
216
+ </body>
217
+ </html>
218
+ ```
219
+
63
220
  ## License
64
221
 
65
222
  MIT
@@ -9,31 +9,172 @@ html, body {
9
9
  overflow-x: clip;
10
10
  }
11
11
 
12
- /* Ensures body takes at least the full height of the viewport (using dynamic viewport height for better mobile support) */
13
-
14
12
  body {
13
+ /* Ensures body takes at least the full height of the viewport (using dynamic viewport height for better mobile support) */
15
14
  min-height: 100dvh;
15
+
16
+ /* Enable hyphenation and font smoothing for better typography */
17
+ hyphens: auto;
18
+ -webkit-font-smoothing: antialiased;
19
+ -moz-osx-font-smoothing: grayscale;
20
+
21
+ /* Make the body a flex container with column layout; main fills space */
22
+ display: flex;
23
+ flex-direction: column;
16
24
  }
17
25
 
26
+ body > main {
27
+ display: flex;
28
+ flex-direction: column;
29
+ flex-grow: 1;
30
+ }
31
+
32
+ .prose a {
33
+ text-underline-offset: 0.1em;
34
+ text-decoration-thickness: 1px;
35
+ }
36
+
37
+ .prose a:hover {
38
+ text-decoration-thickness: 2px;
39
+ }
40
+
41
+ .prose a small,.prose a sup,.prose a sub {
42
+ font-weight: 300;
43
+ /* Workaround to prevent underline */
44
+ display: inline-block;
45
+ }
46
+
47
+ .prose h1 small,.prose h1 sup,.prose h1 sub {
48
+ font-size: 0.5em;
49
+ font-weight: 300;
50
+ }
51
+
52
+ .prose h2:not([class]),.prose h3:not([class]),.prose h4:not([class]) {
53
+ position: relative;
54
+ }
55
+
56
+ .prose h2:not([class])::before {
57
+ content: '';
58
+ display: block;
59
+ position: absolute;
60
+ background-color: rgba(0,0,0,5%);
61
+ }
62
+
63
+ .prose h3:not([class])::before {
64
+ content: '';
65
+ display: block;
66
+ position: absolute;
67
+ background-color: rgba(0,0,0,5%);
68
+ }
69
+
70
+ .prose h4:not([class])::before {
71
+ content: '';
72
+ display: block;
73
+ position: absolute;
74
+ background-color: rgba(0,0,0,5%);
75
+ }
76
+
77
+ .prose h2:not([class]) {
78
+ margin-top: 2em;
79
+ }
80
+
81
+ .prose h2:not([class])::before {
82
+ width: 100vw;
83
+ left: 50%;
84
+ height: 0.4em;
85
+ top: -1em;
86
+ transform: translateX(-50%);
87
+ }
88
+
89
+ .prose h3:not([class])::before {
90
+ width: 10em;
91
+ right: 100%;
92
+ margin-right: 0.5em;
93
+ height: 0.3em;
94
+ top: 50%;
95
+ transform: translateY(-50%);
96
+ background: linear-gradient(to left, rgba(0,0,0,10%), rgba(0,0,0,5%) 10%, transparent);
97
+ }
98
+
99
+ .prose h4:not([class])::before {
100
+ width: 10em;
101
+ right: 100%;
102
+ margin-right: 0.5em;
103
+ height: 0.3em;
104
+ top: 50%;
105
+ transform: translateY(-50%);
106
+ background: linear-gradient(to left, rgba(0,0,0,10%), rgba(0,0,0,5%) 10%, transparent);
107
+ }
108
+
109
+ .prose h4:not([class])::before {
110
+ height: 0.2em;
111
+ }
112
+
113
+ .prose table {
114
+ display: block;
115
+ overflow-x: auto;
116
+ }
117
+
118
+ @media (max-width: 767px) {
119
+
120
+ .prose table {
121
+ padding-inline: 1.5em;
122
+ }
123
+ }
124
+
125
+ .prose th,.prose td {
126
+ padding-top: 1em;
127
+ padding-bottom: 1em;
128
+ /* Workaround to widen particular columns */
129
+ }
130
+
131
+ .prose th hr,.prose td hr {
132
+ min-width: 25ch;
133
+ margin: 0;
134
+ visibility: hidden;
135
+ }
136
+
137
+ /* Workaround for headings in Markdown tables */
138
+
139
+ .prose th big,.prose td big {
140
+ font-weight: bold;
141
+ font-style: italic;
142
+ }
143
+
144
+ .prose blockquote {
145
+ font-weight: 300;
146
+ }
147
+
148
+ .prose blockquote + figcaption {
149
+ margin-top: -1em;
150
+ text-align: right;
151
+ font-style: italic;
152
+ font-weight: 300;
153
+ }
154
+
155
+ .prose blockquote + figcaption::before {
156
+ content: '—';
157
+ margin-right: 0.25em;
158
+ }
159
+
18
160
  /* Breakout CSS - Framework-agnostic utilities for breaking out images and figures */
19
161
 
20
162
  .breakout {
21
- padding-left: 10%;
22
- padding-right: 10%;
163
+ padding-inline: 10%;
23
164
 
24
165
  /* Direct children, or wrapped in <p> for Markdown support */
25
166
  }
26
167
 
27
168
  .breakout > img:not(does-not-exist):not(.does-not-exist),.breakout > picture:not(does-not-exist):not(.does-not-exist),.breakout > figure:not(does-not-exist):not(.does-not-exist),.breakout > canvas:not(does-not-exist):not(.does-not-exist),.breakout > audio:not(does-not-exist):not(.does-not-exist),.breakout > table:not(does-not-exist):not(.does-not-exist),.breakout > pre:not(does-not-exist):not(.does-not-exist),.breakout > iframe:not(does-not-exist):not(.does-not-exist),.breakout > object:not(does-not-exist):not(.does-not-exist),.breakout > embed:not(does-not-exist):not(.does-not-exist),.breakout > video:not(does-not-exist):not(.does-not-exist),.breakout > .breakout-item:not(does-not-exist),.breakout > .breakout-item-max:not(does-not-exist),.breakout > p > img:not(.does-not-exist),.breakout > p > picture:not(.does-not-exist),.breakout > p > figure:not(.does-not-exist),.breakout > p > canvas:not(.does-not-exist),.breakout > p > audio:not(.does-not-exist),.breakout > p > table:not(.does-not-exist),.breakout > p > pre:not(.does-not-exist),.breakout > p > iframe:not(.does-not-exist),.breakout > p > object:not(.does-not-exist),.breakout > p > embed:not(.does-not-exist),.breakout > p > video:not(.does-not-exist),.breakout > p > .breakout-item,.breakout > p > .breakout-item-max {
28
- display: inline-block;
29
- width: auto;
169
+ width: -moz-fit-content;
170
+ width: fit-content;
30
171
  min-width: 100%;
31
172
  max-width: 125%;
32
- transform: translateX(-50%);
33
173
  margin-left: 50%;
174
+ transform: translateX(-50%);
34
175
  }
35
176
 
36
- /* Let inline blocks shrink to fit their content */
177
+ /* Respect inline blocks' min-width */
37
178
 
38
179
  .breakout > img:not(does-not-exist),.breakout > picture:not(does-not-exist),.breakout > figure:not(does-not-exist),.breakout > canvas:not(does-not-exist),.breakout > audio:not(does-not-exist),.breakout > p > img,.breakout > p > picture,.breakout > p > figure,.breakout > p > canvas,.breakout > p > audio {
39
180
  min-width: auto;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anydigital/atomic-bricks",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.5",
4
4
  "description": "Framework-agnostic CSS utility helpers for modern web development",
5
5
  "main": "dist/atomic-bricks.css",
6
6
  "style": "dist/atomic-bricks.css",
@@ -10,6 +10,7 @@
10
10
  ],
11
11
  "scripts": {
12
12
  "build": "postcss src/atomic-bricks.css --no-map -o dist/atomic-bricks.css",
13
+ "dev": "postcss src/atomic-bricks.css -o dist/atomic-bricks.css --watch",
13
14
  "prepublishOnly": "npm run build"
14
15
  },
15
16
  "repository": {
@@ -26,7 +27,7 @@
26
27
  "author": "Anton Staroverov",
27
28
  "license": "MIT",
28
29
  "devDependencies": {
29
- "@anydigital/breakout-css": "^1.0.0-alpha.6",
30
+ "@anydigital/breakout-css": "^1.0.0-alpha.7",
30
31
  "postcss": "^8.4.33",
31
32
  "postcss-cli": "^11.0.0",
32
33
  "postcss-import": "^16.1.1",
package/src/_base.css CHANGED
@@ -3,7 +3,21 @@ html, body {
3
3
  overflow-x: clip;
4
4
  }
5
5
 
6
- /* Ensures body takes at least the full height of the viewport (using dynamic viewport height for better mobile support) */
7
6
  body {
7
+ /* Ensures body takes at least the full height of the viewport (using dynamic viewport height for better mobile support) */
8
8
  min-height: 100dvh;
9
+
10
+ /* Enable hyphenation and font smoothing for better typography */
11
+ hyphens: auto;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+
15
+ /* Make the body a flex container with column layout; main fills space */
16
+ display: flex;
17
+ flex-direction: column;
18
+ > main {
19
+ display: flex;
20
+ flex-direction: column;
21
+ flex-grow: 1;
22
+ }
9
23
  }
package/src/_prose.css ADDED
@@ -0,0 +1,102 @@
1
+ .prose {
2
+ a {
3
+ text-underline-offset: 0.1em;
4
+ text-decoration-thickness: 1px;
5
+ &:hover {
6
+ text-decoration-thickness: 2px;
7
+ }
8
+
9
+ small, sup, sub {
10
+ font-weight: 300;
11
+ /* Workaround to prevent underline */
12
+ display: inline-block;
13
+ }
14
+ }
15
+
16
+ h1 {
17
+ small, sup, sub {
18
+ font-size: 0.5em;
19
+ font-weight: 300;
20
+ }
21
+ }
22
+
23
+ h2:not([class]),
24
+ h3:not([class]),
25
+ h4:not([class]) {
26
+ position: relative;
27
+
28
+ &::before {
29
+ content: '';
30
+ display: block;
31
+ position: absolute;
32
+ background-color: rgba(0,0,0,5%);
33
+ }
34
+ }
35
+ h2:not([class]) {
36
+ margin-top: 2em;
37
+
38
+ &::before {
39
+ width: 100vw;
40
+ left: 50%;
41
+ height: 0.4em;
42
+ top: -1em;
43
+ transform: translateX(-50%);
44
+ }
45
+ }
46
+ h3:not([class]),
47
+ h4:not([class]) {
48
+ &::before {
49
+ width: 10em;
50
+ right: 100%;
51
+ margin-right: 0.5em;
52
+ height: 0.3em;
53
+ top: 50%;
54
+ transform: translateY(-50%);
55
+ background: linear-gradient(to left, rgba(0,0,0,10%), rgba(0,0,0,5%) 10%, transparent);
56
+ }
57
+ }
58
+ h4:not([class]) {
59
+ &::before {
60
+ height: 0.2em;
61
+ }
62
+ }
63
+
64
+ table {
65
+ display: block;
66
+ overflow-x: auto;
67
+ @media (max-width: 767px) {
68
+ padding-inline: 1.5em;
69
+ }
70
+ }
71
+ th, td {
72
+ padding-top: 1em;
73
+ padding-bottom: 1em;
74
+ /* Workaround to widen particular columns */
75
+ hr {
76
+ min-width: 25ch;
77
+ margin: 0;
78
+ visibility: hidden;
79
+ }
80
+ /* Workaround for headings in Markdown tables */
81
+ big {
82
+ font-weight: bold;
83
+ font-style: italic;
84
+ }
85
+ }
86
+
87
+ blockquote {
88
+ font-weight: 300;
89
+
90
+ + figcaption {
91
+ margin-top: -1em;
92
+ text-align: right;
93
+ font-style: italic;
94
+ font-weight: 300;
95
+
96
+ &::before {
97
+ content: '—';
98
+ margin-right: 0.25em;
99
+ }
100
+ }
101
+ }
102
+ }
@@ -4,4 +4,5 @@
4
4
  */
5
5
 
6
6
  @import "_base";
7
+ @import "_prose";
7
8
  @import "@anydigital/breakout-css";
@@ -0,0 +1,18 @@
1
+ {% macro render(gtmId, bodyFallback=false) %}
2
+
3
+ {% if not bodyFallback %}
4
+ <!-- Google Tag Manager -->
5
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
6
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
7
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
8
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
9
+ })(window,document,'script','dataLayer','{{ gtmId }}');</script>
10
+ <!-- End Google Tag Manager -->
11
+ {% else %}
12
+ <!-- Google Tag Manager (noscript) -->
13
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ gtmId }}"
14
+ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
15
+ <!-- End Google Tag Manager (noscript) -->
16
+ {% endif %}
17
+
18
+ {% endmacro %}
@@ -0,0 +1,10 @@
1
+ {% macro render(navPages, currentPageUrl) %}
2
+ <nav>
3
+ {%- for entry in navPages %}
4
+ <a href="{{ entry.url }}" {{ 'aria-current="page"' | safe if entry.url == currentPageUrl }}>
5
+ {{- entry.title -}}
6
+ </a>
7
+ {%- endfor %}
8
+ </nav>
9
+ {% endmacro %}
10
+ {# Compatible with https://www.11ty.dev/docs/plugins/navigation/#bring-your-own-html-render-the-menu-items-manually #}