braccato makes it dead simple to show synced lyrics.
It is the renderer behind Better Lyrics, one of the most used extensions on YouTube Music, published on its own. One custom element, no runtime dependencies.
Quick start
Load the element, load the three stylesheets, point it at a media element and hand it an array. It reads
the clock off whatever it is bound to, drives its own frame loop while that clock runs, and sends a click
on a line back to currentTime.
Importing the stylesheets is the spelling for a bundler, and it is what this page does. Without one they
are three <link> tags at whatever path you serve the package from.
Autoscroll writes scrollTop on the nearest ancestor that scrolls, and on a page where nothing
does, that is the document. If the element is not inside its own scroller, say which one it is:
view.host = { getScrollElement: () => frame }. This page does, because the view fills the
background and taking the wheel there would trap the reader above the docs.
Bring your own lyrics
The array is the whole input, and nothing in this package produces one. Formats are
@braccato/parsers,
which reads TTML, LRC, SRT, QRC and plain text and picks between them by looking at the file.
This page loads the copy built alongside the renderer, so what you see is the parsers as they are now rather than as they were at the last release. Drop a lyrics file anywhere on this page to see it go through.
A theme is a stylesheet
Write CSS against the class names below and the module stays out of it. What the module does read is the
blyrics-* lines inside the comments, which is how a theme changes behaviour without a second
configuration format. An empty theme puts every setting back to its default.
The Theme panel is a text box wired to this property. Edit it and the view changes under you, paused or playing.
The whole surface
Eleven properties, four attributes, four events, and a DOM that themes are allowed to select. This is enough to write the tag. The reasoning behind each decision is in the package notes.
Properties
All of them may be written before the element reaches a document. The renderer is built when it connects, and everything it was handed by then is applied at once.
Attributes
An attribute writes its property and a property never writes back. Reflecting
current-time would put the playback clock into the DOM sixty times a second, and one
attribute reflecting while the rest do not is worse than none of them doing it.
Events
All four bubble and are composed, so an element you put inside your own shadow root still reaches your listener.
The DOM is a contract
These class names are published API, not implementation. Renaming one costs a migration rather than
a refactor. Import them from @braccato/core/constants instead of typing them out.
Custom properties
The ones a theme reaches for first. variables.css declares the rest.
Stylesheets
Yours to load, the way any package's CSS is. Leave them out and you get lines that are in the document and unstyled, rather than lines that are missing.
One thing they do not do for you: the module measures the room the first and last lines need and
publishes it as --blyrics-padding-top and --blyrics-padding-bottom on the
root, but lyrics.css only spends the bottom one. This page supplies the top rule itself.
Light DOM, so your CSS reaches it
The element builds its lines into itself rather than into a shadow root, which is what lets a stylesheet at
document level select them and lets the package's own @property registrations apply. Two
things worth checking in a real browser rather than taking on trust, so this page checked them while it
loaded and printed what it saw.
The tag upgraded
Waiting for the module.
Read once, on load. The theme row is what the markup asked for, before anything on this page touched it.
The cascade reached in
This page's own stylesheet tightens the tracking on the module's container class. A shadow root would put it out of reach.
One view per document
Two views in one document write the same theme element and the same scroll padding, and two in one bundle
share the settings registry and the playback clock, so the second one wins both. Two elements handed the
same theme are fine, and that is the whole line: the module reports the disagreement, not the
company. Both dispatch braccato:error with phase: "conflict" and both read
status === "theme-conflict", and neither stops rendering.