3710 stories
·
3 followers

Doku: Willkommen zu Hause – 20 Jahre Distillery

1 Share

Die Doku, die nun schon etwas älter ist, hat es endlich auf YouTube geschafft und sei jedem, der sich für Clubkultur interessiert, wärmstens an Herz gelegt.

Der Film WILLKOMMEN ZU HAUSE nimmt euch mit auf eine Reise in den einzigartigen Mikrokosmos der Leipziger Club-Legende Distillery. In exklusiven Interviews kommen Veranstalter, DJs und Gäste zu Wort und lassen durch ihre Erinnerungen und Erlebnisse die vergangenen 20 Jahre voller pulsierender Nächte und unvergesslicher Momente wieder aufleben.

Unveröffentlichte Video- und Fotoaufnahmen bringen die Geschichte der Distillery noch einmal auf die große Leinwand – roh, emotional und voller Liebe. Der Film gewährt tiefe Einblicke hinter die Kulissen und zeichnet ein authentisches, oft berührendes und manchmal auch eigenwilliges Bild der Szene.

Aber vor allem macht er eines deutlich: Die Distillery war nie nur ein Club. Sie war Wohnzimmer, Spielplatz, Schutzraum und Experimentierfeld – ein Ort, an dem Nächte zu Geschichten wurden und Gemeinschaft gelebt wurde.

Regie | Stefan Leuschel, Janine Göhring
Produktion | Vier Viertel Film, Janine Göhringa


(Direktlink)

Read the whole story
emrox
10 hours ago
reply
Hamburg, Germany
Share this story
Delete

Where Do You Put the Camera?

1 Share

Ein Video Essay von Tony Zhou und Taylor Ramos, die darin der Frage nachgehen, wie und warum Regisseure in Filmen ihre Kameras so platzieren, wie sie so nun mal platzieren. Ist ja schließlich nicht zufällig gewählt.

If there’s one question that every filmmaker asks themselves, it’s “Where do I put the camera?” Today, we consider the things that really matter when answering that question.


(Direktlink)

Read the whole story
emrox
1 day ago
reply
Hamburg, Germany
Share this story
Delete

📚 Convert E-books into audiobooks with Kokoro

1 Share

Posted on 14 Jan 2025 by Claudio Santini

Kokoro v0.19 is a recently published text-to-speech model with just 82M params and very high-quality output. It's released under Apache licence and was trained on <100 hours of audio. It currently supports american, british english, french, korean, japanese and mandarin, in a bunch of very good voices.

An example of the quality:

I've always dreamed of converting my ebook library into audiobooks. Especially for those niche books that you cannot find in audiobook format. Since Kokoro is pretty fast, I thought this may finally be doable. I've created a small tool called Audiblez (in honor of the popular audiobook platform) that parses .epub files and converts the body of the book into nicely narrated audio files.

On my M2 MacBook Pro, it takes about 2 hours to convert to mp3 the Selfish Gene by Richard Dawkins, which is about 100,000 words (or 600,000 characters), at a rate of about 80 characters per second.

How to install and run

If you have Python 3 on your computer, you can install it with pip. Be aware that it won't work with Python 3.13.

Then you also need to download a couple of additional files in the same folder, which are about ~360MB:

pip install audiblez
wget <a href="https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/kokoro-v0_19.onnx" rel="nofollow">https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/kokoro-v0_19.onnx</a>
wget <a href="https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.json" rel="nofollow">https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.json</a>

Then, to convert an epub file into an audiobook, just run:

audiblez book.epub -l en-gb -v af_sky

It will first create a bunch of book_chapter_1.wav, book_chapter_2.wav, etc. files in the same directory, and at the end it will produce a book.m4b file with the whole book you can listen with VLC or any audiobook player. It will only produce the .m4b file if you have ffmpeg installed on your machine.

Supported Languages

Use -l option to specify the language, available language codes are: 🇺🇸 en-us, 🇬🇧 en-gb, 🇫🇷 fr-fr, 🇯🇵 ja, 🇰🇷 kr and 🇨🇳 cmn.

Supported Voices

Use -v option to specify the voice: available voices are af, af_bella, af_nicole, af_sarah, af_sky, am_adam, am_michael, bf_emma, bf_isabella, bm_george, bm_lewis. You can try them here: https://huggingface.co/spaces/hexgrad/Kokoro-TTS

Chapter Detection

Chapter detection is a bit janky, but it manages to find the core chapters in most .epub I tried, skipping the cover, index, appendix etc.
If you find it doesn't include the chapter you are interested into, try to play with the is_chapter function in the code. Often it skips the preface or intro, and I'm not sure if it's a bug or a feature.

Source

See Audiblez project on GitHub.

There are still some rough edges, but it works well enough for me. Future improvements could include:

  • Better chapter detection, or allow users to include/exclude chapters.
  • Add chapter navigation to m4b file (that looks hard, cause ffmpeg doesn't do it)
  • Add narration for images using some image-to-text model

Code is short enough to be included here:

#!/usr/bin/env python3
# audiblez - A program to convert e-books into audiobooks using
# Kokoro-82M model for high-quality text-to-speech synthesis.
# by Claudio Santini 2025 - <a href="https://claudio.uk" rel="nofollow">https://claudio.uk</a>

import argparse
import sys
import time
import shutil
import subprocess
import soundfile as sf
import ebooklib
import warnings
import re
from pathlib import Path
from string import Formatter
from bs4 import BeautifulSoup
from kokoro_onnx import Kokoro
from ebooklib import epub
from pydub import AudioSegment


def main(kokoro, file_path, lang, voice):
    filename = Path(file_path).name
    with warnings.catch_warnings():
        book = epub.read_epub(file_path)
    title = book.get_metadata('DC', 'title')[0][0]
    creator = book.get_metadata('DC', 'creator')[0][0]
    intro = f'{title} by {creator}'
    print(intro)
    chapters = find_chapters(book)
    print('Found chapters:', [c.get_name() for c in chapters])
    texts = extract_texts(chapters)
    has_ffmpeg = shutil.which('ffmpeg') is not None
    if not has_ffmpeg:
        print('\033[91m' + 'ffmpeg not found. Please install ffmpeg to create mp3 and m4b audiobook files.' + '\033[0m')
    total_chars = sum([len(t) for t in texts])
    print('Started at:', time.strftime('%H:%M:%S'))
    print(f'Total characters: {total_chars:,}')
    print('Total words:', len(' '.join(texts).split(' ')))

    i = 1
    chapter_mp3_files = []
    for text in texts:
        chapter_filename = filename.replace('.epub', f'_chapter_{i}.wav')
        chapter_mp3_files.append(chapter_filename)
        if Path(chapter_filename).exists():
            print(f'File for chapter {i} already exists. Skipping')
            i += 1
            continue
        print(f'Reading chapter {i} ({len(text):,} characters)...')
        if i == 1:
            text = intro + '.\n\n' + text
        start_time = time.time()
        samples, sample_rate = kokoro.create(text, voice=voice, speed=1.0, lang=lang)
        sf.write(f'{chapter_filename}', samples, sample_rate)
        end_time = time.time()
        delta_seconds = end_time - start_time
        chars_per_sec = len(text) / delta_seconds
        remaining_chars = sum([len(t) for t in texts[i - 1:]])
        remaining_time = remaining_chars / chars_per_sec
        print(f'Estimated time remaining: {strfdelta(remaining_time)}')
        print('Chapter written to', chapter_filename)
        print(f'Chapter {i} read in {delta_seconds:.2f} seconds ({chars_per_sec:.0f} characters per second)')
        progress = int((total_chars - remaining_chars) / total_chars * 100)
        print('Progress:', f'{progress}%')
        i += 1
    if has_ffmpeg:
        create_m4b(chapter_mp3_files, filename)


def extract_texts(chapters):
    texts = []
    for chapter in chapters:
        xml = chapter.get_body_content()
        soup = BeautifulSoup(xml, features='lxml')
        chapter_text = ''
        html_content_tags = ['title', 'p', 'h1', 'h2', 'h3', 'h4']
        for child in soup.find_all(html_content_tags):
            inner_text = child.text.strip() if child.text else ""
            if inner_text:
                chapter_text += inner_text + '\n'
        texts.append(chapter_text)
    return texts


def is_chapter(c):
    name = c.get_name().lower()
    part = r"part\d{1,3}"
    if re.search(part, name):
        return True
    ch = r"ch\d{1,3}"
    if re.search(ch, name):
        return True
    if 'chapter' in name:
        return True


def find_chapters(book, verbose=True):
    chapters = [c for c in book.get_items() if c.get_type() == ebooklib.ITEM_DOCUMENT and is_chapter(c)]
    if verbose:
        for item in book.get_items():
            if item.get_type() == ebooklib.ITEM_DOCUMENT:
                # print(f"'{item.get_name()}'" + ', #' + str(len(item.get_body_content())))
                print(f'{item.get_name()}'.ljust(60), str(len(item.get_body_content())).ljust(15), 'X' if item in chapters else '-')
    if len(chapters) == 0:
        print('Not easy to find the chapters, defaulting to all available documents.')
        chapters = [c for c in book.get_items() if c.get_type() == ebooklib.ITEM_DOCUMENT]
    return chapters


def strfdelta(tdelta, fmt='{D:02}d {H:02}h {M:02}m {S:02}s'):
    remainder = int(tdelta)
    f = Formatter()
    desired_fields = [field_tuple[1] for field_tuple in f.parse(fmt)]
    possible_fields = ('W', 'D', 'H', 'M', 'S')
    constants = {'W': 604800, 'D': 86400, 'H': 3600, 'M': 60, 'S': 1}
    values = {}
    for field in possible_fields:
        if field in desired_fields and field in constants:
            values[field], remainder = divmod(remainder, constants[field])
    return f.format(fmt, **values)


def create_m4b(chaptfer_files, filename):
    tmp_filename = filename.replace('.epub', '.tmp.m4a')
    if not Path(tmp_filename).exists():
        combined_audio = AudioSegment.empty()
        for wav_file in chaptfer_files:
            audio = AudioSegment.from_wav(wav_file)
            combined_audio += audio
        print('Converting to Mp4...')
        combined_audio.export(tmp_filename, format="mp4", codec="aac", bitrate="64k")
    final_filename = filename.replace('.epub', '.m4b')
    print('Creating M4B file...')
    proc = subprocess.run(['ffmpeg', '-i', f'{tmp_filename}', '-c', 'copy', '-f', 'mp4', f'{final_filename}'])
    Path(tmp_filename).unlink()
    if proc.returncode == 0:
        print(f'{final_filename} created. Enjoy your audiobook.')
        print('Feel free to delete the intermediary .wav chapter files, the .m4b is all you need.')


def cli_main():
    if not Path('kokoro-v0_19.onnx').exists() or not Path('voices.json').exists():
        print('Error: kokoro-v0_19.onnx and voices.json must be in the current directory. Please download them with:')
        print('wget <a href="https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/kokoro-v0_19.onnx%27" rel="nofollow">https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/kokoro-v0_19.onnx'</a>)
        print('wget <a href="https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.json%27" rel="nofollow">https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.json'</a>)
        sys.exit(1)
    kokoro = Kokoro('kokoro-v0_19.onnx', 'voices.json')
    voices = list(kokoro.get_voices())
    voices_str = ', '.join(voices)
    epilog = 'example:\n' + \
             '  audiblez book.epub -l en-us -v af_sky'
    default_voice = 'af_sky' if 'af_sky' in voices else voices[0]
    parser = argparse.ArgumentParser(epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('epub_file_path', help='Path to the epub file')
    parser.add_argument('-l', '--lang', default='en-gb', help='Language code: en-gb, en-us, fr-fr, ja, ko, cmn')
    parser.add_argument('-v', '--voice', default=default_voice, help=f'Choose narrating voice: {voices_str}')
    if len(sys.argv) == 1:
        parser.print_help(sys.stderr)
        sys.exit(1)
    args = parser.parse_args()
    main(kokoro, args.epub_file_path, args.lang, args.voice)


if __name__ == '__main__':
    cli_main()

Read the whole story
emrox
2 days ago
reply
Hamburg, Germany
Share this story
Delete

Relatively New Things You Should Know about HTML Heading Into 2025

1 Share

Comments

Read the whole story
emrox
3 days ago
reply
Hamburg, Germany
Share this story
Delete

New Front-End Features For Designers In 2025

1 Comment and 2 Shares

Component-specific styling, styling parents based on their children, relative colors — the web platform is going through exciting times, and many things that required JavaScript in the past can today be achieved with one simple line of HTML and CSS.

As we are moving towards 2025, it’s a good time to revisit some of the incredible new technologies that are broadly available and supported in modern browsers today. Let’s dive right in and explore how they can simplify your day-to-day work and help you build modern UI components.

Table of Contents

Below you’ll find quick jumps to topics you may be interested in, or skip the table of contents.

CSS Container Queries And Style Queries

Component-specific styling? What has long sounded like a dream to any developer, is slowly but surely becoming reality. Thanks to container queries, we can now query the width and style of the container in which components live.

As Una Kravets points out in her introduction to style queries, this currently only works with CSS custom property values, but there are already some real-world use cases where style queries shine: They come in particularly handy when you have a reusable component with multiple variations or when you don’t have control over all of your styles but need to apply changes in certain cases.

If you want to dive deeper into what’s possible with container style queries and the things we can — maybe — look forward to in the future, also be sure to take a look at Geoff Graham’s post. He dug deep into the more nuanced aspects of style queries and summarized the things that stood out to him.

No More Typographic Orphans And Widows

We all know those headlines where the last word breaks onto a new line and stands there alone, breaking the visual and looking, well, odd. Of course, there’s the good ol’ <br> to break the text manually or a <span> to divide the content into different parts. But have you heard of text-wrap: balance already?

By applying the text-wrap: balance property, the browser will automatically calculate the number of words and divide them equally between two lines — perfect for page titles, card titles, tooltips, modals, and FAQs, for example. Ahmad Shadeed wrote a helpful guide to text-wrap: balance in which he takes a detailed look at the property and how it can help you make your headlines look more consistent.

When dealing with large blocks of text, such as paragraphs, you might want to look into text-wrap: pretty to prevent orphans on the last line.

Auto Field-Sizing For Forms

Finding just the right size for an input field usually involves a lot of guesswork — or JavaScript — to count characters and increase the field’s height or width as a user enters text. CSS field-sizing is here to change that. With field-sizing, we can auto-grow inputs and text areas, but also auto-shrink short select menus, so the form always fits content size perfectly. All we need to make it happen is one line of CSS.

Adam Argyle summarized everything you need to know about field-sizing, exploring in detail how field-sizing affects different <form> elements. To prevent your input fields from becoming too small or too large, it is also a good idea to insert some additional styles that keep them in shape. Adam shares a code snippet that you can copy-and-paste right away.

Making Hidden Content Searchable

Accordions are a popular UI pattern, but they come with a caveat: The content inside the collapsed sections is impossible to search with find-in-page search. By using the hidden=until-found attribute and the beforematch event, we can solve the problem and even make the content accessible to search engines.

As Joey Arhar explains in his guide to making collapsed content searchable, you can replace the styles that hide the section with the hidden=until-found attribute. If your page also has another state that needs to be kept in sync with whether or not your section is revealed, he recommends adding a beforematch event listener. It will be fired on the hidden=until-found element right before the element is revealed by the browser.

Styling Groups Within Select Menus

It’s a small upgrade for the <select> element, but a mighty one: We can now add <hr> into the list of select options, and they will appear as separators to help visually break up the options in the list.

If you want to refine things further, also be sure to take a look at <optgroup>. The HTML element lets you group options within a <select> element by adding a subheading for each group.

Simpler Snapping For Scrollable Containers

Sometimes, you need a quick and easy way to make an element a scrollable container. CSS scroll snap makes it possible. The CSS feature enables us to create a well-controlled scrolling experience that lets users precisely swipe left and right and snap to a specific item in the container. No JavaScript required.

Ahmad Shadeed wrote a practical guide that walks you step by step through the process of setting up a container with scroll snap. You can use it to create image galleries, avatar lists, or other components where you want a user to scroll and snap through the content, whether it’s horizontally or vertically.

Anchor Positioning For Tooltips And Popovers

Whether you use it for footnotes, tooltips, connector lines, visual cross-referencing, or dynamic labels in charts, the CSS Anchor Positioning API enables us to natively position elements relative to other elements, known as anchors.

In her introduction to the CSS Anchor Positioning API, Una Kravets summarized in detail how anchor positioning works. She takes a closer look at the mechanism behind anchor positioning, how to tether to one and multiple anchors, and how to size and position an anchor-positioned element based on the size of its anchor. Browser support is still limited, so you might want to use the API with some precautions. Una’s guide includes what to watch out for.

High-Definition Colors With OKLCH And OKLAB

With high-definition colors with LCH, okLCH, LAB, and okLAB that give us access to 50% more colors, the times of RGB/HSL might be over soon. To get you familiar with the new color spaces, Vitaly wrote a quick overview of what you need to know.

Both OKLCH and OKLAB are based on human perception and can specify any color the human eye can see. While OKLAB works best for rich gradients, OKLCH is a fantastic fit for color palettes in design systems. OKLCH/OKLAB colors are fully supported in Chrome, Edge, Safari, Firefox, and Opera. Figma doesn’t support them yet.

Relative Colors In CSS

Let’s say you have a background color and want to reduce its luminosity by 25%, or you want to use a complementary color without having to calculate it yourself. The relative color syntax (RCS) makes it possible to create a new color based on a given color.

To derive and compute a new color, we can use the from keyword for color functions (color(), hsl(), oklch(), etc.) to modify the values of the input color. Adam Argyle shares some code snippets of what this looks like in practice, or check the spec for more details.

Smooth Transitions With The View Transitions API

There are a number of use cases where a smooth visual transition can make the user experience more engaging. When a thumbnail image on a product listing page transitions into a full-size image on the product detail page, for example, or when you have a fixed navigation bar that stays in place as you navigate from one page to another. The View Transitions API helps us create seamless visual transitions between different views on a site.

View transitions can be triggered not only on a single document but also between two different documents. Both rely on the same principle: The browser takes snapshots of the old and new states, the DOM gets updated while rendering is suppressed, and the transitions are powered by CSS Animations. The only difference lies in how you trigger them, as Bramus Van Damme explains in his guide to the View Transitions API. A good alternative to single page apps that often rely on heavy JavaScript frameworks.

Exclusive Accordions

The ‘exclusive accordion’ is a variation of the accordion component. It only allows one disclosure widget to be open at the same time, so when a user opens a new one, the one that is already open will be closed automatically to save space. Thanks to CSS, we can now create the effect without a single line of JavaScript.

To build an exclusive accordion, we need to add a name attribute to the <details> elements. When this attribute is used, all <details> elements that have the same name value form a semantic group and behave as an exclusive accordion. Bramus Van Damme summarized in detail how it works.

Live And Late Validation

When we use :valid and :invalid to apply styling based on a user’s input, there’s a downside: a form control that is required and empty will match :invalid even if a user hasn’t started interacting with it yet. To prevent this from happening, we usually had to write stateful code that keeps track of input a user has changed. But not anymore.

With :user-valid and :user-invalid, we now have a native CSS solution that handles all of this automatically. Contrary to :valid and :invalid, the :user-valid and :user-invalid pseudo-classes give users feedback about mistakes only after they have changed the input. :user-valid and :user-invalid work with input, select, and textarea controls.

Smooth Scrolling Behavior

Imagine you have a scrolling box and a series of links that target an anchored position inside the box. When a user clicks on one of the links, it will take them to the content section inside the scrolling box — with a rather abrupt jump. The scroll-behavior property makes the scrolling transition a lot smoother, only with CSS.

When setting the scroll-behavior value to smooth, the scrolling box will scroll in a smooth fashion using a user-agent-defined easing function over a user-agent-defined period of time. Of course, you can also use scroll-behavior: auto, and the scrolling box will scroll instantly.

Making Focus Visible

Focus styles are essential to help keyboard users navigate a page. However, for mouse users, it can be irritating when a focus ring appears around a button or link as they click on it. :focus-visible is here to help us create the best experience for both user groups: It displays focus styles for keyboard users and hides them for mouse users.

:focus-visible applies while an element matches the :focus pseudo-class and the User Agent determines via heuristics that the focus should be made visible on the element. Curious how it works in practice? MDN Web Docs highlights the differences between :focus and :focus-visible, what you need to consider accessibility-wise, and how to provide a fallback for old browser versions that don’t support :focus-visible.

Styling Parents Based On Children

Historically, CSS selectors have worked in a top-down fashion, allowing us to style a child based on its parent. The new CSS pseudo-class :has works the other way round: We can now style a parent based on its children. But that’s not all yet. Josh W. Comeau wrote a fantastic introduction to :has in which he explores real-world use cases that show what the pseudo-class is capable of.

:has is not limited to parent-child relationships or direct siblings. Instead, it lets us style one element based on the properties or status of any other element in a totally different container. And it can be used as a sort of global event listener, as Josh shows — to disable scrolling on a page when a modal is open or to create a JavaScript-free dark mode toggle, for example.

Interpolate Between Values For Type And Spacing

CSS comparison functions min(), max(), and clamp() are today supported in all major browsers, providing us with an effective way to create dynamic layouts with fluid type scales, grids, and spacing systems.

To get you fit for using the functions in your projects right away, Ahmad Shadeed wrote a comprehensive guide in which he explains everything you need to know about min(), max(), and clamp(), with practical examples and use cases and including all the points of confusion you might encounter.

If you’re looking for a quick and easy way to create fluid scales, the Fluid Type Scale Calculator by Utopia has got your back. All you need to do is define min and max viewport widths and the number of scale steps, and the calculator provides you with a responsive preview of the scale and the CSS code snippet.

Reliable Dialog And Popover

If you’re looking for a quick way to create a modal or popup, the <dialog> HTML element finally offers a native (and accessible!) solution to help you get the job done. It represents a modal or non-modal dialog box or other interactive component, such as a confirmation prompt or a subwindow used to enter data.

While modal dialog boxes interrupt interaction with a page, non-modal dialog boxes allow interaction with the page while the dialog is open. Adam Argyle published some code snippets that show how <dialog> can block pop-ups and popovers for non-blocking menus, out of the box.

Responsive HTML Video And Audio

In 2014, media attribute support for HTML video sources was deleted from the HTML standard. Last year, it made a comeback, which means that we can use media queries for delivering responsive HTML videos.

Scott Jehl summarized how responsive HTML video — and even audio — works, what you need to consider when writing the markup, and what other types of media queries can be used in combination with HTML video.

The Right Virtual Keyboard On Mobile

It’s a small detail, but one that adds to a well-considered user experience: displaying the most comfortable touchscreen keyboard to help a user enter their information without having to switch back and forth to insert numbers, punctuation, or special characters like an @ symbol.

To show the right keyboard layout, we can use inputmode. It instructs the browser which keyboard to display and supports values for numeric, telephone, decimal, email, URL, and search keyboards. To further improve the UX, we can add the enterkeyhint attribute: it adjusts the text on the Enter key. If no enterkeyhint is used, the user agent might use contextual information from the inputmode attribute.

A Look Into The Future

As we are starting to adopt all of these shiny new front-end features in our projects, the web platform is, of course, constantly evolving — and there are some exciting things on the horizon already! For example, we are very close to getting masonry layout, fully customizable drop-downs with <selectmenu>, and text-box trimming for adjusting fonts to be perfectly aligned within the grid. Kudos to all the wonderful people who are working tirelessly to push the web forward! 👏

In the meantime, we hope you found something helpful in this post that you can apply to your product or application right away. Happy tinkering!

Smashing Weekly Newsletter

You want to stay on top of what’s happening in the world of front-end and UX? With our weekly newsletter, we aim to bring you useful, practical tidbits and share some of the helpful things that folks are working on in the web industry. Every issue is curated, written, and edited with love and care. No third-party mailings or hidden advertising.

Also, when you subscribe, you really help us pay the bills. Thank you for your kind support!



Read the whole story
emrox
3 days ago
reply
good overview
Hamburg, Germany
alvinashcraft
16 days ago
reply
West Grove, PA
Share this story
Delete

Static initialization blocks in JavaScript classes (#tilPost)

1 Share

I've been reading Jake's guide on custom elements and when/where to define a new custom element. It's a good and quick read, so go check it out!

His final custom element snippet includes something I haven't seen before.

class MyElement extends HTMLElement {
  // Static class property
  static tag = "my-element";

  // Static class method
  static define(tag = this.tag) {
    // ...
  }

  // 👇 What's this is?
  static {
    // ...
  }
}

The class defines two static class members. One is a class property (tag), and the other is a class method (define). Both static class members can be called without initializing the class and creating an instance.

class MyElement extends HTMLElement {
  static tag = "my-element";

  static define(tag = this.tag) {
    console.log('define was called...')
  }
}

// access the `tag` property without initializing a new class instance
console.log(MyElement.tag); // 'my-element'

// call the `define` method without initializing a new class instance
MyElement.define(); // 'define was called...'

Cool, that's what static is for. But what's up with this nameless static block?

Apparently, JavaScript classes support static initialization blocks. These initialization blocks are called during class initialization (don't confuse them with when you initialize a new object of a class).

So, whenever the JavaScript parser rolls over and parses the class MyElement, it'll call its static block. That's neat!

class MyElement extends HTMLElement {
  static {
    console.log('class was initialized...')
  }
} 

// automatically logs "class was initialized..."

Static initialization blocks can be extremely handy for conditionally registering custom elements (as Jake showed) or dynacially (uhm... dynamically?! 😅) setting up a shared property across class instances. I could think of a shared database connection or something like this.

But can we all use static initialization blocks today?

MDN Compat Data (source)
Browser support info for Class static initialization blocks
chrome chrome_android edge firefox firefox_android safari safari_ios samsunginternet_android webview_android
94 94 94 93 93 16.4 16.4 17.0 94

Apparently, static initialization blocks have been part of ECMAScript 2022, and they're supported across all browsers. Well, today I learned!


Reply to Stefan
Read the whole story
emrox
4 days ago
reply
Hamburg, Germany
Share this story
Delete
Next Page of Stories