Created
August 31, 2025 17:25
-
-
Save tak-dcxi/38a45b765969fcf2c790827c5564cbe7 to your computer and use it in GitHub Desktop.
Fit Text
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| fit-text { | |
| display: block flow; | |
| &::part(svg) { | |
| display: block flow; | |
| inline-size: 100%; | |
| block-size: auto; | |
| } | |
| &::part(text) { | |
| fill: currentcolor; | |
| } | |
| &:not(:defined) { | |
| visibility: hidden; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class FitTextElement extends HTMLElement { | |
| private shadow: ShadowRoot; | |
| private svg: SVGSVGElement; | |
| private textElement: SVGTextElement; | |
| private observer: ResizeObserver | null = null; | |
| constructor() { | |
| super(); | |
| this.shadow = this.attachShadow({ mode: "open" }); | |
| this.shadow.innerHTML = ` | |
| <svg part="svg"> | |
| <text part="text" dominant-baseline="text-before-edge"></text> | |
| </svg> | |
| `; | |
| this.svg = this.shadow.querySelector("svg")!; | |
| this.textElement = this.shadow.querySelector("text")!; | |
| } | |
| connectedCallback(): void { | |
| this.updateText(); | |
| this.setupObserver(); | |
| this.observeTextChanges(); | |
| } | |
| disconnectedCallback(): void { | |
| this.observer?.disconnect(); | |
| } | |
| private updateText(): void { | |
| this.textElement.textContent = this.textContent; | |
| this.updateViewBox(); | |
| } | |
| private updateViewBox(): void { | |
| requestAnimationFrame(() => { | |
| const bbox = this.textElement.getBBox(); | |
| const newViewBox = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`; | |
| const currentViewBox = this.svg.getAttribute("viewBox"); | |
| if (currentViewBox === newViewBox) return; | |
| this.svg.setAttribute("viewBox", newViewBox); | |
| }); | |
| } | |
| private setupObserver(): void { | |
| this.observer = new ResizeObserver(() => { | |
| requestAnimationFrame(() => { | |
| this.updateViewBox(); | |
| }); | |
| }); | |
| this.observer.observe(this); | |
| } | |
| private observeTextChanges(): void { | |
| const textObserver = new MutationObserver(() => { | |
| this.updateText(); | |
| }); | |
| textObserver.observe(this, { | |
| childList: true, | |
| characterData: true, | |
| subtree: true | |
| }); | |
| } | |
| } | |
| customElements.define("fit-text", FitTextElement); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment