@anydigital/breakout-css 0.11.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) 2025 Anton Staroverov
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,139 @@
1
+ # breakout-css
2
+
3
+ Modern CSS utilities to easily break-out / hang / pop-out / bleed images, tables, iframes, and other figures from their parent container.
4
+
5
+ ## Installation
6
+
7
+ ### From CDN
8
+
9
+ ```html
10
+ <link
11
+ rel="stylesheet"
12
+ href="https://cdn.jsdelivr.net/npm/@anydigital/breakout-css@1/dist/breakout.css"
13
+ />
14
+ ```
15
+
16
+ ### From Source
17
+
18
+ ```css
19
+ @import "@anydigital/breakout-css";
20
+ ```
21
+
22
+ ^ This is supported by Tailwind v4!
23
+
24
+ ## Usage
25
+
26
+ ### Basic Usage
27
+
28
+ ```html
29
+ <div class="breakout">
30
+ <h1>Article Title</h1>
31
+ <p>Lorem ipsum dolor sit amet...</p>
32
+
33
+ <!-- This image will automatically break out -->
34
+ <img src="hero.jpg" alt="Hero image" />
35
+
36
+ <p>More content here...</p>
37
+ </div>
38
+ ```
39
+
40
+ ### Supported Elements
41
+
42
+ The breakout effect automatically applies to direct children or elements wrapped in `<p>` tags:
43
+
44
+ **Inline blocks:**
45
+
46
+ - `img`, `picture`, `figure`, `canvas`, `audio`
47
+
48
+ **Larger blocks:**
49
+
50
+ - `table` (responsive with horizontal scroll support), `pre`
51
+ - `iframe`, `object`, `embed`, `video`
52
+
53
+ **Custom utility classes:**
54
+
55
+ - Elements with `.breakout-item` or `.breakout-item-max` class
56
+
57
+ ### Headings & Dividers
58
+
59
+ For decorative headings and full-width dividers, use the `.breakout-headings` class. This adds a subtle accent line to the left of headings and makes horizontal rules span the full viewport width:
60
+
61
+ ```html
62
+ <div class="breakout-headings">
63
+ <h2>Section Title</h2>
64
+ <p>Some content...</p>
65
+
66
+ <hr />
67
+
68
+ <h3>Subheading</h3>
69
+ <p>More content...</p>
70
+ </div>
71
+ ```
72
+
73
+ The extension applies to the following elements (when they don't have other classes):
74
+
75
+ - `h2`, `h3`, `h4` (adds decorative accent line)
76
+ - `hr` (breaks out to full viewport width)
77
+
78
+ _Note: The decorative accent on headings is automatically hidden if the heading immediately follows an `<hr>` to avoid visual overlap._
79
+
80
+ ### Manual Breakout
81
+
82
+ For elements that don't automatically break out, use the `.breakout-item` class:
83
+
84
+ ```html
85
+ <div class="breakout">
86
+ <p>Regular content...</p>
87
+
88
+ <div class="breakout-item">
89
+ <iframe src="https://example.com/embed"></iframe>
90
+ </div>
91
+
92
+ <p>More content...</p>
93
+ </div>
94
+ ```
95
+
96
+ ### Force Maximum Width
97
+
98
+ By default, breakout elements use `width: fit-content` with `max-width: 125%`, allowing them to size between 100% and 125% width based on their content. To force an element to always use the full 125% breakout width, use `.breakout-item-max`:
99
+
100
+ ```html
101
+ <div class="breakout">
102
+ <p>Regular content...</p>
103
+
104
+ <!-- This will always be 125% width, never smaller -->
105
+ <img src="wide-image.jpg" class="breakout-item-max" alt="Wide image" />
106
+
107
+ <p>More content...</p>
108
+ </div>
109
+ ```
110
+
111
+ Note: `.breakout-item-max` uses `width: 125% !important` to override default sizing.
112
+
113
+ ## How It Works
114
+
115
+ The `.breakout` container acts as a content wrapper that:
116
+
117
+ 1. Sets a smart `max-width: calc(10% + 65ch + 10%)` to ensure an optimal reading line length (approx. 65 characters).
118
+ 2. Applies `padding-inline: 10%` to create the necessary gutter space for breakout elements to extend into.
119
+
120
+ The breakout effect on elements is achieved by:
121
+
122
+ 1. Setting `width: fit-content` with `min-width: 100%` and `max-width: 125%` (inline blocks like `img`, `picture`, `figure`, `canvas`, and `audio` use `min-width: auto` instead). Tables are handled specially to be full-bleed (`max-width: 100vw`) with internal horizontal padding (`7.5%`) and horizontal scroll support.
123
+ 2. Using `margin-left: 50%` to position from the center of the container
124
+ 3. Using `transform: translateX(-50%)` to shift it left by half its width
125
+
126
+ This combination allows elements to extend beyond their parent container (up to 125% width) while remaining visually centered.
127
+
128
+ The `.breakout-headings` utility works by:
129
+
130
+ 1. Adding a `::before` pseudo-element to headings (`h2-h4`) positioned to the left.
131
+ 2. Using a `100vw` width and negative translation on `hr::before` to create a full-width divider.
132
+
133
+ ### Markdown Support
134
+
135
+ The breakout effect works on direct children of `.breakout`, or elements wrapped in `<p>` tags (for Markdown compatibility where images are often wrapped in paragraphs).
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,150 @@
1
+ /* Breakout CSS - Framework-agnostic utilities for breaking out images and figures */
2
+
3
+ .breakout {
4
+ /* Prepare the container for breakout elements */
5
+ padding-inline: 10%;
6
+ max-width: calc(10% + 65ch + 10%);
7
+
8
+ /* Direct children, or wrapped in <p> for Markdown support */
9
+ }
10
+
11
+ .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 {
12
+ width: -moz-fit-content;
13
+ width: fit-content;
14
+ min-width: 100%;
15
+ max-width: 125%;
16
+ margin-left: 50%;
17
+ transform: translateX(-50%);
18
+ }
19
+
20
+ /* Respect inline blocks' min-width */
21
+
22
+ .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 {
23
+ min-width: auto;
24
+ }
25
+
26
+ /* Tables are so special :( */
27
+
28
+ .breakout > table:not(does-not-exist):not(.does-not-exist),.breakout > p > table:not(.does-not-exist) {
29
+ /* .does-not-exist is here to avoid !important below @TODO */
30
+
31
+ /* Let them full-bleed */
32
+ width: -moz-max-content;
33
+ width: max-content;
34
+ min-width: auto;
35
+ max-width: 100vw;
36
+ padding-inline: 7.5%;
37
+
38
+ /* Let them scroll */
39
+ display: block;
40
+ overflow-x: auto;
41
+ -webkit-overflow-scrolling: touch; /* Smooth scroll for iOS */
42
+ }
43
+
44
+ /* Max out the width of the element */
45
+
46
+ .breakout > .breakout-item-max:not(does-not-exist),.breakout > p > .breakout-item-max {
47
+ width: 125% !important; /* !important is for cases like figure.breakout-item-max @TODO */
48
+ }
49
+
50
+ .breakout-headings h2:not([class]),.breakout-headings h3:not([class]),.breakout-headings h4:not([class]),.breakout-headings hr {
51
+ position: relative;
52
+ }
53
+
54
+ .breakout-headings h2:not([class])::before {
55
+ content: "";
56
+ display: block;
57
+ position: absolute;
58
+ background-color: rgba(0, 0, 0, 5%);
59
+ }
60
+
61
+ .breakout-headings h3:not([class])::before {
62
+ content: "";
63
+ display: block;
64
+ position: absolute;
65
+ background-color: rgba(0, 0, 0, 5%);
66
+ }
67
+
68
+ .breakout-headings h4:not([class])::before {
69
+ content: "";
70
+ display: block;
71
+ position: absolute;
72
+ background-color: rgba(0, 0, 0, 5%);
73
+ }
74
+
75
+ .breakout-headings hr:not(.does-not-exist)::before {
76
+ content: "";
77
+ display: block;
78
+ position: absolute;
79
+ background-color: rgba(0, 0, 0, 5%);
80
+ }
81
+
82
+ .breakout-headings h2:not([class])::before {
83
+ width: 10em;
84
+ right: 100%;
85
+ margin-right: 1rem;
86
+ height: 0.3em;
87
+ top: 50%;
88
+ transform: translateY(-50%);
89
+ background: linear-gradient(
90
+ to left,
91
+ rgba(0, 0, 0, 10%),
92
+ rgba(0, 0, 0, 5%) 10%,
93
+ transparent
94
+ );
95
+ }
96
+
97
+ .breakout-headings h3:not([class])::before {
98
+ width: 10em;
99
+ right: 100%;
100
+ margin-right: 1rem;
101
+ height: 0.3em;
102
+ top: 50%;
103
+ transform: translateY(-50%);
104
+ background: linear-gradient(
105
+ to left,
106
+ rgba(0, 0, 0, 10%),
107
+ rgba(0, 0, 0, 5%) 10%,
108
+ transparent
109
+ );
110
+ }
111
+
112
+ .breakout-headings h4:not([class])::before {
113
+ width: 10em;
114
+ right: 100%;
115
+ margin-right: 1rem;
116
+ height: 0.3em;
117
+ top: 50%;
118
+ transform: translateY(-50%);
119
+ background: linear-gradient(
120
+ to left,
121
+ rgba(0, 0, 0, 10%),
122
+ rgba(0, 0, 0, 5%) 10%,
123
+ transparent
124
+ );
125
+ }
126
+
127
+ .breakout-headings hr {
128
+ height: 0.75rem;
129
+ border: none;
130
+ overflow: visible;
131
+ }
132
+
133
+ .breakout-headings hr::before {
134
+ width: 100vw;
135
+ left: 50%;
136
+ height: 100%;
137
+ transform: translateX(-50%);
138
+ }
139
+
140
+ .breakout-headings hr + h2::before {
141
+ display: none !important;
142
+ }
143
+
144
+ .breakout-headings hr + h3::before {
145
+ display: none !important;
146
+ }
147
+
148
+ .breakout-headings hr + h4::before {
149
+ display: none !important;
150
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@anydigital/breakout-css",
3
+ "version": "0.11.0",
4
+ "description": "Modern CSS utilities to easily break-out / hang / pop-out / bleed images, tables, iframes, and other figures from their parent container",
5
+ "keywords": [
6
+ "css",
7
+ "breakout",
8
+ "bleed",
9
+ "images",
10
+ "tables",
11
+ "figures",
12
+ "responsive",
13
+ "tailwind",
14
+ "utilities"
15
+ ],
16
+ "files": [
17
+ "dist",
18
+ "src",
19
+ "postcss.config.js"
20
+ ],
21
+ "style": "./src/breakout.css",
22
+ "exports": {
23
+ ".": "./src/breakout.css",
24
+ "./dist": "./dist/breakout.css"
25
+ },
26
+ "scripts": {
27
+ "build": "postcss src/breakout.css --no-map -o dist/breakout.css",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/anydigital/breakout-css.git"
33
+ },
34
+ "author": "Anton Staroverov",
35
+ "license": "MIT",
36
+ "devDependencies": {
37
+ "postcss": "^8.4.0",
38
+ "postcss-cli": "^11.0.0",
39
+ "postcss-preset-env": "^10.6.0"
40
+ }
41
+ }
@@ -0,0 +1,12 @@
1
+ module.exports = {
2
+ plugins: {
3
+ 'postcss-preset-env': {
4
+ stage: 3,
5
+ features: {
6
+ 'nesting-rules': true,
7
+ 'is-pseudo-class': true,
8
+ },
9
+ preserve: false,
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,108 @@
1
+ /* Breakout CSS - Framework-agnostic utilities for breaking out images and figures */
2
+
3
+ .breakout {
4
+ /* Prepare the container for breakout elements */
5
+ padding-inline: 10%;
6
+ max-width: calc(10% + 65ch + 10%);
7
+
8
+ /* Direct children, or wrapped in <p> for Markdown support */
9
+ & > *,
10
+ & > p > * {
11
+ &:is(
12
+ /* Inline blocks */
13
+ img, picture, figure, canvas, audio,
14
+ /* Larger blocks */
15
+ table,
16
+ pre,
17
+ iframe, object, embed, video,
18
+ /* Custom utility classes for other tags that need to be broken out */
19
+ .breakout-item,
20
+ .breakout-item-max
21
+ ) {
22
+ width: fit-content;
23
+ min-width: 100%;
24
+ max-width: 125%;
25
+ margin-left: 50%;
26
+ transform: translateX(-50%);
27
+ }
28
+
29
+ /* Respect inline blocks' min-width */
30
+ &:is(img, picture, figure, canvas, audio) {
31
+ min-width: auto;
32
+ }
33
+
34
+ /* Tables are so special :( */
35
+ &:is(table):not(.does-not-exist) {
36
+ /* .does-not-exist is here to avoid !important below @TODO */
37
+
38
+ /* Let them full-bleed */
39
+ width: max-content;
40
+ min-width: auto;
41
+ max-width: 100vw;
42
+ padding-inline: 7.5%;
43
+
44
+ /* Let them scroll */
45
+ display: block;
46
+ overflow-x: auto;
47
+ -webkit-overflow-scrolling: touch; /* Smooth scroll for iOS */
48
+ }
49
+
50
+ /* Max out the width of the element */
51
+ &.breakout-item-max {
52
+ width: 125% !important; /* !important is for cases like figure.breakout-item-max @TODO */
53
+ }
54
+ }
55
+ }
56
+
57
+ .breakout-headings {
58
+ h2:not([class]),
59
+ h3:not([class]),
60
+ h4:not([class]),
61
+ hr {
62
+ position: relative;
63
+
64
+ &::before {
65
+ content: "";
66
+ display: block;
67
+ position: absolute;
68
+ background-color: rgba(0, 0, 0, 5%);
69
+ }
70
+ }
71
+ h2:not([class]),
72
+ h3:not([class]),
73
+ h4:not([class]) {
74
+ &::before {
75
+ width: 10em;
76
+ right: 100%;
77
+ margin-right: 1rem;
78
+ height: 0.3em;
79
+ top: 50%;
80
+ transform: translateY(-50%);
81
+ background: linear-gradient(
82
+ to left,
83
+ rgba(0, 0, 0, 10%),
84
+ rgba(0, 0, 0, 5%) 10%,
85
+ transparent
86
+ );
87
+ }
88
+ }
89
+ hr {
90
+ height: 0.75rem;
91
+ border: none;
92
+ overflow: visible;
93
+
94
+ &::before {
95
+ width: 100vw;
96
+ left: 50%;
97
+ height: 100%;
98
+ transform: translateX(-50%);
99
+ }
100
+ }
101
+ hr + h2,
102
+ hr + h3,
103
+ hr + h4 {
104
+ &::before {
105
+ display: none !important;
106
+ }
107
+ }
108
+ }