├── LICENSE.md
├── README.md
├── demos
├── jagged-edge-mask
│ ├── iconoclasts.png
│ ├── index.html
│ ├── site.css
│ └── worklet.js
├── jagged-edge
│ ├── index.html
│ ├── site.css
│ └── worklet.js
├── placeholder-box-props
│ ├── index.html
│ ├── site.css
│ └── worklet.js
├── placeholder-box
│ ├── index.html
│ ├── site.css
│ └── worklet.js
├── polka-dot-fade-animated
│ ├── index.html
│ ├── site.css
│ └── worklet.js
├── polka-dot-fade
│ ├── index.html
│ ├── site.css
│ └── worklet.js
└── solid-color
│ ├── index.html
│ ├── site.css
│ └── worklet.js
├── favicon.png
├── index.html
└── site.css
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Will Boyd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hello-houdini
2 |
3 | Some demos using Houdini's CSS Paint API.
4 |
5 | The CSS Paint API only works over https or localhost . If you want to play with these demos locally, I recommend [http-server](https://www.npmjs.com/package/http-server) to easily serve them over localhost .
6 |
7 | To learn more, please read the tutorial article, [Say Hello to Houdini and the CSS Paint API](https://codersblock.com/blog/say-hello-to-houdini-and-the-css-paint-api/). You can see all of these demos in action on the [Hello Houdini demo site](https://lonekorean.github.io/hello-houdini/).
8 |
--------------------------------------------------------------------------------
/demos/jagged-edge-mask/iconoclasts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lonekorean/hello-houdini/cce3b7c802211679315370e57bf9a20c515fce57/demos/jagged-edge-mask/iconoclasts.png
--------------------------------------------------------------------------------
/demos/jagged-edge-mask/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Jagged Edge with Mask
7 |
8 |
9 |
10 |
11 |
12 |
35 |
36 |
37 |
45 |
46 |
47 |
48 | This Houdini demo requires the CSS Paint API , CSS Properties and Values API , and Typed OM .
49 | Use Chrome and go to chrome://flags and enable Experimental Web Platform features .
50 | Also make sure you're serving over https or localhost .
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/demos/jagged-edge-mask/site.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after {
2 | box-sizing: border-box;
3 | }
4 |
5 | html, body {
6 | height: 100%;
7 | }
8 |
9 | body {
10 | margin: 0 40px;
11 | background: #000;
12 | font: 1rem/1.25 'Mukta Mahee', sans-serif;
13 | }
14 |
15 | .container {
16 | display: grid;
17 | grid-template-columns: repeat(2, 1fr);
18 | grid-gap: 40px;
19 | align-items: center;
20 | height: 100%;
21 | }
22 |
23 | .slot {
24 | display: flex;
25 | justify-content: center;
26 | position: relative;
27 | height: 300px;
28 | border: 1px solid #fff;
29 | background-image: linear-gradient(to right, #444, #000);
30 | }
31 |
32 | .jagged {
33 | --tooth-width: 80px;
34 | --tooth-height: 30px;
35 | -webkit-mask-image: paint(jagged-edge);
36 |
37 | position: absolute;
38 | top: 30%;
39 | bottom: 0;
40 | width: 100%;
41 | }
42 |
43 | .slot:nth-child(1) .jagged {
44 | background-image: linear-gradient(to right, #22c1c3, #fdbb2d);
45 | }
46 |
47 | .slot:nth-child(2) .jagged {
48 | /* pixel art from Iconoclasts, fun game! http://www.playiconoclasts.com/ */
49 | background-image: url('iconoclasts.png');
50 | background-size: cover;
51 | background-position: 50% 0;
52 | }
53 |
54 | .warning {
55 | display: none;
56 | position: absolute;
57 | top: 10px;
58 | left: 10px;
59 | right: 10px;
60 | padding: 20px;
61 | border-top: 4px dashed #e8590c;
62 | background-color: #ffe8cc;
63 | color: #e8590c;
64 | text-align: center;
65 | }
66 |
67 | .no-support .warning {
68 | display: block;
69 | }
70 |
71 | .warning .fas {
72 | margin-bottom: 1rem;
73 | font-size: 1.5rem;
74 | }
75 |
--------------------------------------------------------------------------------
/demos/jagged-edge-mask/worklet.js:
--------------------------------------------------------------------------------
1 | class JaggedEdgePainter {
2 | static get inputProperties() {
3 | return ['--tooth-width', '--tooth-height'];
4 | }
5 |
6 | paint(ctx, size, props) {
7 | let toothWidth = props.get('--tooth-width').value;
8 | let toothHeight = props.get('--tooth-height').value;
9 |
10 | // lots of math to ensure teeth are collectively centered
11 | let spaceBeforeCenterTooth = (size.width - toothWidth) / 2;
12 | let teethBeforeCenterTooth = Math.ceil(spaceBeforeCenterTooth / toothWidth);
13 | let totalTeeth = teethBeforeCenterTooth * 2 + 1;
14 | let startX = spaceBeforeCenterTooth - teethBeforeCenterTooth * toothWidth;
15 |
16 | // start drawing teeth from left
17 | ctx.beginPath();
18 | ctx.moveTo(startX, toothHeight);
19 |
20 | // draw the top zig-zag for all the teeth
21 | for (let i = 0; i < totalTeeth; i++) {
22 | let x = startX + toothWidth * i;
23 | ctx.lineTo(x + toothWidth / 2, 0);
24 | ctx.lineTo(x + toothWidth, toothHeight);
25 | }
26 |
27 | // surround the area below the teeth and fill it all in
28 | ctx.lineTo(size.width, size.height);
29 | ctx.lineTo(0, size.height);
30 | ctx.closePath();
31 | ctx.fill();
32 | }
33 | }
34 |
35 | registerPaint('jagged-edge', JaggedEdgePainter);
36 |
--------------------------------------------------------------------------------
/demos/jagged-edge/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Jagged Edge
7 |
8 |
9 |
10 |
11 |
12 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | --tooth-width: 50px;
42 | --tooth-height: 25px;
43 |
44 |
45 |
46 |
47 |
48 |
49 | --tooth-width: 2rem;
50 | --tooth-height: 3rem;
51 |
52 |
53 |
54 |
55 |
56 |
57 | --tooth-width: calc(33vw - 31px);
58 | --tooth-height: 2em;
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | This Houdini demo requires the CSS Paint API , CSS Properties and Values API , and Typed OM .
67 | Use Chrome and go to chrome://flags and enable Experimental Web Platform features .
68 | Also make sure you're serving over https or localhost .
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/demos/jagged-edge/site.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after {
2 | box-sizing: border-box;
3 | }
4 |
5 | html, body {
6 | height: 100%;
7 | }
8 |
9 | body {
10 | margin: 0 20px;
11 | background-image: linear-gradient(to bottom right, #74ebd5, #acb6e5);
12 | font: 1rem/1.25 'Mukta Mahee', sans-serif;
13 | }
14 |
15 | .container {
16 | display: grid;
17 | grid-template-columns: repeat(3, 1fr);
18 | grid-gap: 20px;
19 | align-items: center;
20 | height: 100%;
21 | }
22 |
23 | .slot {
24 | display: flex;
25 | justify-content: center;
26 | position: relative;
27 | height: 200px;
28 | border: 2px solid #000;
29 | }
30 |
31 | .jagged {
32 | background: paint(jagged-edge);
33 |
34 | position: absolute;
35 | top: 30%;
36 | bottom: 0;
37 | width: 100%;
38 | color: #fff;
39 | }
40 |
41 | .slot:nth-child(1) .jagged {
42 | --tooth-width: 50px;
43 | --tooth-height: 25px;
44 | }
45 |
46 | .slot:nth-child(2) .jagged {
47 | --tooth-width: 2rem;
48 | --tooth-height: 3rem;
49 | }
50 |
51 | .slot:nth-child(3) .jagged {
52 | --tooth-width: calc(33vw - 31px);
53 | --tooth-height: 2em;
54 | }
55 |
56 | .info {
57 | position: absolute;
58 | bottom: 10px;
59 | left: 10px;
60 | }
61 |
62 | .warning {
63 | display: none;
64 | position: absolute;
65 | top: 10px;
66 | left: 10px;
67 | right: 10px;
68 | padding: 20px;
69 | border-top: 4px dashed #e8590c;
70 | background-color: #ffe8cc;
71 | color: #e8590c;
72 | text-align: center;
73 | }
74 |
75 | .no-support .warning {
76 | display: block;
77 | }
78 |
79 | .warning .fas {
80 | margin-bottom: 1rem;
81 | font-size: 1.5rem;
82 | }
83 |
--------------------------------------------------------------------------------
/demos/jagged-edge/worklet.js:
--------------------------------------------------------------------------------
1 | class JaggedEdgePainter {
2 | static get inputProperties() {
3 | return ['--tooth-width', '--tooth-height'];
4 | }
5 |
6 | paint(ctx, size, props) {
7 | let toothWidth = props.get('--tooth-width').value;
8 | let toothHeight = props.get('--tooth-height').value;
9 |
10 | // lots of math to ensure teeth are collectively centered
11 | let spaceBeforeCenterTooth = (size.width - toothWidth) / 2;
12 | let teethBeforeCenterTooth = Math.ceil(spaceBeforeCenterTooth / toothWidth);
13 | let totalTeeth = teethBeforeCenterTooth * 2 + 1;
14 | let startX = spaceBeforeCenterTooth - teethBeforeCenterTooth * toothWidth;
15 |
16 | // start drawing teeth from left
17 | ctx.beginPath();
18 | ctx.moveTo(startX, toothHeight);
19 |
20 | // draw the top zig-zag for all the teeth
21 | for (let i = 0; i < totalTeeth; i++) {
22 | let x = startX + toothWidth * i;
23 | ctx.lineTo(x + toothWidth / 2, 0);
24 | ctx.lineTo(x + toothWidth, toothHeight);
25 | }
26 |
27 | // surround the area below the teeth and fill it all in
28 | ctx.lineTo(size.width, size.height);
29 | ctx.lineTo(0, size.height);
30 | ctx.closePath();
31 | ctx.fill();
32 | }
33 | }
34 |
35 | registerPaint('jagged-edge', JaggedEdgePainter);
36 |
--------------------------------------------------------------------------------
/demos/placeholder-box-props/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Placeholder Box with Input Properties
7 |
8 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
36 |
37 |
38 |
39 | This Houdini demo requires the CSS Paint API and Typed OM .
40 | Use Chrome and go to chrome://flags and enable Experimental Web Platform features .
41 | Also make sure you're serving over https or localhost .
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/demos/placeholder-box-props/site.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after {
2 | box-sizing: border-box;
3 | }
4 |
5 | html, body {
6 | height: 100%;
7 | }
8 |
9 | body {
10 | margin: 0 20px;
11 | background-image: linear-gradient(to bottom, #70e1f5, #ffd194);
12 | font: 1rem/1.25 'Mukta Mahee', sans-serif;
13 | }
14 |
15 | .container {
16 | display: grid;
17 | grid-template-columns: repeat(3, 1fr);
18 | grid-gap: 20px;
19 | align-items: center;
20 | height: 100%;
21 | }
22 |
23 | .slot {
24 | display: flex;
25 | justify-content: center;
26 | }
27 |
28 | .placeholder {
29 | background-image: paint(placeholder-box-props);
30 |
31 | background-color: #fff;
32 | width: 120px;
33 | height: 120px;
34 | max-height: 100vh;
35 | overflow: auto;
36 | resize: both;
37 | }
38 |
39 | .slot:nth-child(1) .placeholder {
40 | border: 2px solid #fd7e14;
41 | }
42 |
43 | .slot:nth-child(2) .placeholder {
44 | border: 6px solid #228be6;
45 | }
46 |
47 | .slot:nth-child(3) .placeholder {
48 | border: 1rem solid #e64980;
49 | }
50 |
51 | .warning {
52 | display: none;
53 | position: absolute;
54 | top: 10px;
55 | left: 10px;
56 | right: 10px;
57 | padding: 20px;
58 | border-top: 4px dashed #e8590c;
59 | background-color: #ffe8cc;
60 | color: #e8590c;
61 | text-align: center;
62 | }
63 |
64 | .no-support .warning {
65 | display: block;
66 | }
67 |
68 | .warning .fas {
69 | margin-bottom: 1rem;
70 | font-size: 1.5rem;
71 | }
72 |
--------------------------------------------------------------------------------
/demos/placeholder-box-props/worklet.js:
--------------------------------------------------------------------------------
1 | class PlaceholderBoxPropsPainter {
2 | static get inputProperties() {
3 | return ['border-top-width', 'border-top-color'];
4 | }
5 |
6 | paint(ctx, size, props) {
7 | // default values
8 | ctx.lineWidth = 2;
9 | ctx.strokeStyle = '#666';
10 |
11 | // set line width to top border width (if exists)
12 | let borderTopWidthProp = props.get('border-top-width');
13 | if (borderTopWidthProp) {
14 | ctx.lineWidth = borderTopWidthProp.value;
15 | }
16 |
17 | // set stroke style to top border color (if exists)
18 | let borderTopColorProp = props.get('border-top-color');
19 | if (borderTopColorProp) {
20 | ctx.strokeStyle = borderTopColorProp.toString();
21 | }
22 |
23 | // draw line from top left to bottom right
24 | ctx.beginPath();
25 | ctx.moveTo(0, 0);
26 | ctx.lineTo(size.width, size.height);
27 | ctx.stroke();
28 |
29 | // draw line from top right to bottom left
30 | ctx.beginPath();
31 | ctx.moveTo(size.width, 0);
32 | ctx.lineTo(0, size.height);
33 | ctx.stroke();
34 | }
35 | }
36 |
37 | registerPaint('placeholder-box-props', PlaceholderBoxPropsPainter);
38 |
--------------------------------------------------------------------------------
/demos/placeholder-box/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Placeholder Box
7 |
8 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
26 |
27 |
28 | Awesome Mockup
29 | This uses placeholder boxes created with the CSS Paint API. Neat! You can resize them by dragging their bottom right corners.
30 |
35 | That's all. Thanks!
36 |
37 |
40 |
41 |
42 |
43 |
44 | This Houdini demo requires the CSS Paint API .
45 | Use Chrome .
46 | Also make sure you're serving over https or localhost .
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/demos/placeholder-box/site.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after {
2 | box-sizing: border-box;
3 | }
4 |
5 | body {
6 | margin: 20px;
7 | color: #333;
8 | background-color: #eee;
9 | font: 1rem/1.25 'Mukta Mahee', sans-serif;
10 | }
11 |
12 | .placeholder {
13 | background-image: paint(placeholder-box);
14 |
15 | background-color: #fff;
16 | max-width: 100%;
17 | border: 2px solid #666;
18 | overflow: auto;
19 | resize: both;
20 | }
21 |
22 | header {
23 | width: 600px;
24 | height: 100px;
25 | margin: 0 auto;
26 | }
27 |
28 | .content-wrapper {
29 | display: flex;
30 | width: 600px;
31 | margin: 40px auto 0;
32 | font-size: 1.25rem;
33 | line-height: 1;
34 | }
35 |
36 | main {
37 | width: 440px;
38 | }
39 |
40 | h1 {
41 | margin: 0;
42 | font-weight: normal;
43 | }
44 |
45 | p {
46 | line-height: 1.25;
47 | }
48 |
49 | .row {
50 | margin: 0 10px 0 -10px;
51 | }
52 |
53 | .row::after {
54 | content: '';
55 | display: block;
56 | clear: both;
57 | }
58 |
59 | main .placeholder {
60 | float: left;
61 | width: 100px;
62 | height: 100px;
63 | margin: 10px;
64 | }
65 |
66 | aside {
67 | width: 140px;
68 | margin-left: 20px;
69 | }
70 |
71 | aside .placeholder {
72 | width: 140px;
73 | height: 320px;
74 | margin: 0 auto;
75 | }
76 |
77 | .warning {
78 | display: none;
79 | position: absolute;
80 | top: 10px;
81 | left: 10px;
82 | right: 10px;
83 | padding: 20px;
84 | border-top: 4px dashed #e8590c;
85 | background-color: #ffe8cc;
86 | color: #e8590c;
87 | text-align: center;
88 | }
89 |
90 | .no-support .warning {
91 | display: block;
92 | }
93 |
94 | .warning .fas {
95 | margin-bottom: 1rem;
96 | font-size: 1.5rem;
97 | }
98 |
--------------------------------------------------------------------------------
/demos/placeholder-box/worklet.js:
--------------------------------------------------------------------------------
1 | class PlaceholderBoxPainter {
2 | paint(ctx, size) {
3 | ctx.lineWidth = 2;
4 | ctx.strokeStyle = '#666';
5 |
6 | // draw line from top left to bottom right
7 | ctx.beginPath();
8 | ctx.moveTo(0, 0);
9 | ctx.lineTo(size.width, size.height);
10 | ctx.stroke();
11 |
12 | // draw line from top right to bottom left
13 | ctx.beginPath();
14 | ctx.moveTo(size.width, 0);
15 | ctx.lineTo(0, size.height);
16 | ctx.stroke();
17 | }
18 | }
19 |
20 | registerPaint('placeholder-box', PlaceholderBoxPainter);
21 |
--------------------------------------------------------------------------------
/demos/polka-dot-fade-animated/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Animated Polka Dot Fade
7 |
8 |
9 |
10 |
11 |
12 |
41 |
42 |
43 |
44 | Hover/Tap Me
45 |
46 |
47 |
48 |
49 | This Houdini demo requires the CSS Paint API , CSS Properties and Values API , and Typed OM .
50 | Use Chrome and go to chrome://flags and enable Experimental Web Platform features .
51 | Also make sure you're serving over https or localhost .
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/demos/polka-dot-fade-animated/site.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after {
2 | box-sizing: border-box;
3 | }
4 |
5 | html, body {
6 | height: 100%;
7 | }
8 |
9 | body {
10 | margin: 0;
11 | background: #000;
12 | font: 1rem/1.25 'Mukta Mahee', sans-serif;
13 | }
14 |
15 | .polka-dot {
16 | --dot-spacing: 20px;
17 | --dot-fade-offset: 0%;
18 | --dot-color: #fc466b;
19 | background-image: paint(polka-dot-fade);
20 |
21 | display: flex;
22 | justify-content: center;
23 | align-items: center;
24 | position: absolute;
25 | top: 40px;
26 | right: 40px;
27 | bottom: 40px;
28 | left: 40px;
29 | color: #fff;
30 | font-size: 6rem;
31 | text-transform: uppercase;
32 | cursor: pointer;
33 | }
34 |
35 | .polka-dot:hover, .polka-dot:focus {
36 | outline: 1px solid #fff;
37 | animation: pulse 2s ease-out 6 alternate;
38 | }
39 |
40 | @keyframes pulse {
41 | from {
42 | --dot-fade-offset: 0%;
43 | --dot-color: #fc466b;
44 | }
45 | to {
46 | --dot-fade-offset: 100%;
47 | --dot-color: #3f5efb;
48 | }
49 | }
50 |
51 | .warning {
52 | display: none;
53 | position: absolute;
54 | top: 10px;
55 | left: 10px;
56 | right: 10px;
57 | padding: 20px;
58 | border-top: 4px dashed #e8590c;
59 | background-color: #ffe8cc;
60 | color: #e8590c;
61 | text-align: center;
62 | }
63 |
64 | .no-support .warning {
65 | display: block;
66 | }
67 |
68 | .warning .fas {
69 | margin-bottom: 1rem;
70 | font-size: 1.5rem;
71 | }
72 |
--------------------------------------------------------------------------------
/demos/polka-dot-fade-animated/worklet.js:
--------------------------------------------------------------------------------
1 | class PolkaDotFadePainter {
2 | static get inputProperties() {
3 | return ['--dot-spacing', '--dot-fade-offset', '--dot-color'];
4 | }
5 |
6 | paint(ctx, size, props) {
7 | let spacing = props.get('--dot-spacing').value;
8 | let fadeOffset = props.get('--dot-fade-offset').value;
9 | let color = props.get('--dot-color').toString();
10 |
11 | ctx.fillStyle = color;
12 | for (let y = 0; y < size.height + spacing; y += spacing) {
13 | for (let x = 0; x < size.width + spacing; x += spacing * 2) {
14 | // every other row shifts x to create staggered dots
15 | let staggerX = x + ((y / spacing) % 2 === 1 ? spacing : 0);
16 |
17 | // calculate dot radius based on horizontal position and fade offset
18 | let fadeRelativeX = staggerX - size.width * fadeOffset / 100;
19 | let radius = spacing * Math.max(Math.min(1 - fadeRelativeX / size.width, 1), 0);
20 |
21 | // draw dot
22 | ctx.beginPath();
23 | ctx.arc(staggerX, y, radius, 0, 2 * Math.PI);
24 | ctx.fill();
25 | }
26 | }
27 | }
28 | }
29 |
30 | registerPaint('polka-dot-fade', PolkaDotFadePainter);
31 |
--------------------------------------------------------------------------------
/demos/polka-dot-fade/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Polka Dot Fade
7 |
8 |
9 |
10 |
11 |
12 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | This Houdini demo requires the CSS Paint API , CSS Properties and Values API , and Typed OM .
48 | Use Chrome and go to chrome://flags and enable Experimental Web Platform features .
49 | Also make sure you're serving over https or localhost .
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/demos/polka-dot-fade/site.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after {
2 | box-sizing: border-box;
3 | }
4 |
5 | html, body {
6 | height: 100%;
7 | }
8 |
9 | body {
10 | margin: 0;
11 | background: #000;
12 | font: 1rem/1.25 'Mukta Mahee', sans-serif;
13 | }
14 |
15 | .polka-dot {
16 | --dot-spacing: 20px;
17 | --dot-fade-offset: 0%;
18 | --dot-color: #40e0d0;
19 | background-image: paint(polka-dot-fade);
20 |
21 | position: absolute;
22 | top: 40px;
23 | right: 40px;
24 | bottom: 40px;
25 | left: 40px;
26 | }
27 |
28 | .warning {
29 | display: none;
30 | position: absolute;
31 | top: 10px;
32 | left: 10px;
33 | right: 10px;
34 | padding: 20px;
35 | border-top: 4px dashed #e8590c;
36 | background-color: #ffe8cc;
37 | color: #e8590c;
38 | text-align: center;
39 | }
40 |
41 | .no-support .warning {
42 | display: block;
43 | }
44 |
45 | .warning .fas {
46 | margin-bottom: 1rem;
47 | font-size: 1.5rem;
48 | }
49 |
--------------------------------------------------------------------------------
/demos/polka-dot-fade/worklet.js:
--------------------------------------------------------------------------------
1 | class PolkaDotFadePainter {
2 | static get inputProperties() {
3 | return ['--dot-spacing', '--dot-fade-offset', '--dot-color'];
4 | }
5 |
6 | paint(ctx, size, props) {
7 | let spacing = props.get('--dot-spacing').value;
8 | let fadeOffset = props.get('--dot-fade-offset').value;
9 | let color = props.get('--dot-color').toString();
10 |
11 | ctx.fillStyle = color;
12 | for (let y = 0; y < size.height + spacing; y += spacing) {
13 | for (let x = 0; x < size.width + spacing; x += spacing * 2) {
14 | // every other row shifts x to create staggered dots
15 | let staggerX = x + ((y / spacing) % 2 === 1 ? spacing : 0);
16 |
17 | // calculate dot radius based on horizontal position and fade offset
18 | let fadeRelativeX = staggerX - size.width * fadeOffset / 100;
19 | let radius = spacing * Math.max(Math.min(1 - fadeRelativeX / size.width, 1), 0);
20 |
21 | // draw dot
22 | ctx.beginPath();
23 | ctx.arc(staggerX, y, radius, 0, 2 * Math.PI);
24 | ctx.fill();
25 | }
26 | }
27 | }
28 | }
29 |
30 | registerPaint('polka-dot-fade', PolkaDotFadePainter);
31 |
--------------------------------------------------------------------------------
/demos/solid-color/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Fairly Useless Solid Color
7 |
8 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | This Houdini demo requires the CSS Paint API , CSS Properties and Values API , and Typed OM .
30 | Use Chrome and go to chrome://flags and enable Experimental Web Platform features .
31 | Also make sure you're serving over https or localhost .
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/demos/solid-color/site.css:
--------------------------------------------------------------------------------
1 | *, *::before, *::after {
2 | box-sizing: border-box;
3 | }
4 |
5 | html, body {
6 | height: 100%;
7 | }
8 |
9 | body {
10 | margin: 0;
11 | background: #000;
12 | font: 1rem/1.25 'Mukta Mahee', sans-serif;
13 | }
14 |
15 | .solid {
16 | background-image: paint(solid-color, #c0eb75);
17 |
18 | position: absolute;
19 | top: 40px;
20 | right: 40px;
21 | bottom: 40px;
22 | left: 40px;
23 | }
24 |
25 | .warning {
26 | display: none;
27 | position: absolute;
28 | top: 10px;
29 | left: 10px;
30 | right: 10px;
31 | padding: 20px;
32 | border-top: 4px dashed #e8590c;
33 | background-color: #ffe8cc;
34 | color: #e8590c;
35 | text-align: center;
36 | }
37 |
38 | .no-support .warning {
39 | display: block;
40 | }
41 |
42 | .warning .fas {
43 | margin-bottom: 1rem;
44 | font-size: 1.5rem;
45 | }
46 |
--------------------------------------------------------------------------------
/demos/solid-color/worklet.js:
--------------------------------------------------------------------------------
1 | class SolidColorPainter {
2 | static get inputArguments() {
3 | return [''];
4 | }
5 |
6 | paint(ctx, size, props, args) {
7 | ctx.fillStyle = args[0].toString();
8 | ctx.fillRect(0, 0, size.width, size.height);
9 | }
10 | }
11 |
12 | registerPaint('solid-color', SolidColorPainter);
13 |
--------------------------------------------------------------------------------
/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lonekorean/hello-houdini/cce3b7c802211679315370e57bf9a20c515fce57/favicon.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Hello Houdini
8 |
9 |
10 |
11 |
12 |
13 | Hello Houdini
14 |
15 | Some demos using Houdini's CSS Paint API.
16 | Check out the tutorial article for these demos or grab the source code from this GitHub repo .
17 |
18 |
19 |
20 | Placeholder Box
21 | Simple paint worklet that draws an X to create a placeholder box.
22 |
23 |
24 | Placeholder Box with Input Properties
25 | Uses input properties to match the element's border styles.
26 |
27 |
28 | Jagged Edge
29 | Reads custom properties (CSS variables) to customize how the paint worklet draws.
30 |
31 |
32 | Jagged Edge with Mask
33 | Combines a paint worklet with -webkit-mask-image to create a "knockout" effect.
34 |
35 |
36 | Fairly Useless Solid Color
37 | Emulates background-color. Fairly useless, but shows how to use input arguments.
38 |
39 |
40 | Polka Dot Fade
41 | Creates a fading polka dot pattern using custom properties.
42 |
43 |
44 | Animated Polka Dot Fade
45 | Brings motion to the polka dot pattern by animating custom properties in CSS.
46 |
47 |
48 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/site.css:
--------------------------------------------------------------------------------
1 | body {
2 | max-width: 820px;
3 | margin: 0 auto;
4 | padding: 20px;
5 | color: #495057;
6 | background-color: #fff;
7 | font: 1.25rem/1.5 'Mukta Mahee', sans-serif;
8 | }
9 |
10 | h1 {
11 | margin: 0;
12 | font-size: 3rem;
13 | font-weight: normal;
14 | }
15 |
16 | a {
17 | color: #00a89a;
18 | text-decoration: none;
19 | }
20 |
21 | a:hover {
22 | text-decoration: underline;
23 | }
24 |
25 | li {
26 | padding-bottom: 20px;
27 | }
28 |
29 | .notes {
30 | border-top: 1px dashed #adb5bd;
31 | }
32 |
--------------------------------------------------------------------------------