JS is useful
but often overused
HTML & CSS are lighter
- No JS
- No build tools
- No npm packages
- Just HTML & CSS
HTML Tricks
- Content Targeting
- Accordians
- Form Validations
Content Targeting
<!-- Links to another page or website -->
<a href="/about">Other page</a>
<a href="https://google.com">Other website</a>
<!-- A File or Resource -->
<a href="/img/my_image.jpg" download>Download Image</a>
<a href="/resources/info.pdf">View PDF</a>
<!-- An ID -->
<a href="#main">Skip to main content</a>
<a href="/about#contact">Contact the team</a>
.my_element:target {
background: pink;
}
<body>
<header><nav>
<a href="#home">Home</a>
...
<a href="#contact">Contact</a>
</nav></header>
<main>
<section id="home"></section>
...
<section id="contact"></section>
</main>
</body>
section {
display: none;
&:target {
display: block;
}
}
- 2010
- 2015
- 2008
- 2006
- 2011
Full Disclosure…
Accordians
<details>
<summary>This bit is clickable to expand/collapse</summary>
<p>This content is hidden but can then be expanded or re-collapsed.</p>
</details>
<details>
<summary>Why is HTML better than JS?</summary>
<p>HTML functionality...</p>
...
</details>
<details>
<summary>Why is CSS better than JS?</summary>
<p>CSS is built for styling...</p>
</details>
- 2011
- 2020
- 2012
- 2016
Full Disclosure…
Form Validations
Required Fields
<input name="email" required />
Data Format
<input type="date" name="due_date">
<input type="tel" name="phone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" />
- 2010
- 2015
- 2010
- 2011
- 2012
- 2011
- 2015
- 2017
- 2011
- 2012
Full Disclosure…
CSS Tricks
- Nesting
- Range Slider
- Testimonials
- Toggle Switch
- Image Carousel
Nesting
div {
& p {
color: red;
}
}
- 2023
- 2023
- 2023
- 2023
Range Slider
<input type="range" min="0" max="10" />
div:has(p:nth-child(1)) {
--children: 1;
}
...
div:has(p:nth-child(4)) {
--children: 4;
}
div:has(p:nth-child(5)) {
--children: 5;
}
<input type="range" min="0" max="10" list="options" />
<datalist id="options">
<option value="0">Not good</option>
<option value="5">🤷♀️</option>
<option value="10">Stupendous</option>
</datalist>
<input
type="range"
min="0"
max="10"
list="js_skills"
/>
<datalist id="js_skills">
<option value="0">Never used it before</option>
<option value="2">Used it once or twice</option>
<option value="4">Can follow a tutorial</option>
<option value="6">Fine as long as it works</option>
<option value="8">Use it regularly</option>
<option value="10">I use JS for everything</option>
</datalist>
&:has(input[type="range"] + datalist option:nth-child(1)) {
--opts: 1;
}
&:has(input[type="range"] + datalist option:nth-child(2)) {
--opts: 2;
}
...
&:has(input[type="range"] + datalist option:nth-child(10)) {
--opts: 10;
}
input[type="range"] {
width: calc(100% - (100% / var(--opts)));
& + datalist {
display: grid;
grid-template-columns: 50px repeat(var(--opts),1fr 2em);
& option {
grid-column-end: span 3;
grid-column-start: var(--column);
&:nth-child(2n) {
grid-row-start: 2;
}
}
}
}
- 2010
- 2015
- 2008
- 2013
- 2012
- 2018
- 2020
- 2019
- 2023
- 2022
- 2022
- 2022
- 2023
Testimonials
<input type="radio" id="t_1" name="testimonials" />
<label for="t_1">Quote 1</label>
<blockquote>
<!-- Quote here -->
</blockquote>
.testimonials {
display: grid;
grid-template-rows: 1fr auto;
grid-template-columns: 1fr repeat(var(--num_test), auto) 1fr;
justify-content: center;
&:has(blockquote:nth-of-type(1)) {
--num_test: 1;
}
...
}
blockquote {
grid-column: 1 / -1;
grid-row: 1 / 2;
}
label {
order: 2;
grid-row: 2;
}
blockquote {
pointer-events: none;
opacity: 0;
}
input[type="radio"]:checked {
& + label + blockquote {
opacity: 1;
pointer-events: auto;
}
}
label {
position: relative;
cursor: pointer;
&:before, &:after {
height: 20px;
width: 20px;
border-radius: 50%;
border: 2px solid var(--green);
}
}
label {
&:after {
background: radial-gradient(
circle,
var(--green) calc(100% - 6px),
transparent calc(100% - 6px)
);
position: absolute;
}
}
label:first-of-type {
grid-column: 2;
}
.testimonials:not(:has(input[type="radio"]:checked)) {
& blockquote:first-of-type {
opacity: 1;
pointer-events: auto;
}
}
- 2022
- 2022
- 2022
- 2023
- 2021
- 2021
- 2015
- 2020
- 2017
- 2017
- 2017
- 2017
- 2017
Full Disclosure…
Toggle Switch
<div class="toggle">
<input id="off" type="radio" name="switch" value="off" checked />
<label for="off">Off</label>
<input id="on" type="radio" name="switch" value="on" />
<label for="on">On</label>
<span class="switch"></span>
</div>
.toggle {
display: flex;
justify-content: center;
align-items: center;
}
input[type="radio"][value="on"] + label {
order: 2;
}
.switch::before {
position: absolute;
top: -3px;
left: -3px;
width: 1.7em;
height: 1.7em;
border: 3px solid currentcolor;
border-radius: 50%;
background: var(--background);
}
input[type="radio"][value="on"]:checked ~ .switch {
background: currentcolor;
&::before {
right: -3px;
left: auto;
}
}
input[type="radio"] + label::before {
position: absolute;
inset: 0;
z-index: 4;
}
input[type="radio"][value="on"] + label::before {
right: 0;
left: calc(-1 * (1ch + 2.7em));
}
input[type="radio"][value="off"] + label::before {
right: calc(-1 * (1ch + 2.7em));
left: 0;
}
input[type="radio"]:checked + label::before {
content: '';
}
body:has(.theme_toggle input[type="radio"][value="light"]:checked) {
--background: #f7f0eb;
--neutral: #0d0d0d;
...
}
body:has(.theme_toggle input[type="radio"][value="dark"]:checked) {
--background: #0d0d0d;
--neutral: #f7f0eb;
...
}
- 2022
- 2022
- 2022
- 2023
Full Disclosure…
Image Carousel
<input type="radio" name="images" value="i_1" id="img_1" checked />
<label for="img_1">Image 1</label>
<div class="image">
<img src="img_1.jpg" alt="" />
</div>
.carousel {
display: grid;
grid-template-columns: 4em 1fr 4em;
grid-template-rows: 300px;
grid-template-areas: 'previous image next';
overflow: hidden;
max-width: 100%;
}
.image {
grid-area: image;
opacity: 0;
}
label {
display: none;
}
input[type="radio"]:checked + label + .image {
opacity: 1;
}
.carousel:not(:has(input[type="radio"]:checked)) {
& input[type="radio"]:first-of-type + label + .image {
opacity: 1;
}
}
input[type="radio"]:checked + label + .image + input[type="radio"] + label {
display: block;
grid-area: next;
&::before {
content: '➡️';
}
}
input[type="radio"]:not(:checked):has(+ label + .image + input:checked) + label {
display: block;
grid-area: previous;
&::before {
content: '⬅️';
}
}
- 2017
- 2017
- 2017
- 2017
- 2017
- 2022
- 2022
- 2022
- 2023
Full Disclosure…
nojs.amyskapers.dev
JS is useful but not the answer
Can you do it with HTML?
Can you do it with CSS?
Can JS make it better?
Can some JS be removed?
Do we really need to track this?
Questions?
kapers.dev/nojs
Thank You👏