<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexander</title>
    <description>The latest articles on DEV Community by Alexander (@aalexander).</description>
    <link>https://dev.to/aalexander</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4048003%2F5c3f336e-34b0-4866-9161-fa211a2962af.png</url>
      <title>DEV Community: Alexander</title>
      <link>https://dev.to/aalexander</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aalexander"/>
    <language>en</language>
    <item>
      <title>JS Tools — Five content utilities in one HTML file</title>
      <dc:creator>Alexander</dc:creator>
      <pubDate>Tue, 28 Jul 2026 12:12:20 +0000</pubDate>
      <link>https://dev.to/aalexander/js-tools-five-content-utilities-in-one-html-file-1lnj</link>
      <guid>https://dev.to/aalexander/js-tools-five-content-utilities-in-one-html-file-1lnj</guid>
      <description>&lt;h1&gt;
  
  
  ⚡ JS Tools
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A single-file, dependency-free toolkit of five content utilities for editors, SEOs and developers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FAQ lists → WPBakery accordion shortcodes · strip inline styles and stray &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags from Word/Docs paste · bulk-edit link classes and &lt;code&gt;rel&lt;/code&gt; · generate slugs · check SEO character limits. Live as you type, entirely in your browser. No uploads, no build step, no network requests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;MIT&lt;/code&gt; · &lt;code&gt;0 dependencies&lt;/code&gt; · &lt;code&gt;1 file&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contents:&lt;/strong&gt; Quick start · Interface · FAQ → Accordion · HTML Cleaner · Link Tool · Slug Maker · Counter · Shared controls · Internals · Add your own tool · Theming · Clipboard caveats · Limitations · Roadmap&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this exists
&lt;/h2&gt;

&lt;p&gt;Content teams repeat the same five chores daily: reformatting an FAQ doc into a page-builder accordion, scrubbing Word paste-in garbage, adding &lt;code&gt;rel="nofollow noopener"&lt;/code&gt; to a batch of links, slugifying a title, checking a meta description against Google's truncation point.&lt;/p&gt;

&lt;p&gt;Every one of those has a dozen ad-supported websites offering to help — right after you paste your client's unpublished content onto someone else's server. &lt;strong&gt;JS Tools does all five locally.&lt;/strong&gt; Open it from a USB stick on a plane if you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;code&gt;jstools.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Double-click it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No &lt;code&gt;npm install&lt;/code&gt;, no bundler, no CDN links — all HTML, CSS and JS is inline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hosting (optional):&lt;/strong&gt; drop it on GitHub Pages, Netlify, or a &lt;code&gt;/tools/&lt;/code&gt; folder on your own site. &lt;strong&gt;HTTPS unlocks the Paste buttons&lt;/strong&gt; — see clipboard caveats. Nothing persists between refreshes, so keep it in a pinned tab.&lt;/p&gt;




&lt;h2&gt;
  
  
  Interface
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────┐
│  ⚡ JS Tools                                  🌙 Theme   │  header
├──────────────────────────────────────────────────────────┤
│  [FAQ] [Cleaner] [Links] [Slug] [Counter]                │  tabs
├──────────────────────────────────────────────────────────┤
│  ☑ option  ☑ option  [field]              [▶ Convert]    │  toolbar
├───────────────────────────┬──────────────────────────────┤
│  INPUT      📋 ⧉ ✕        │  OUTPUT     ⇄ ⭳ ⧉ ✕          │
│  textarea                 │  textarea                    │
│  120 chars · 22 words     │  340 chars · 48 words        │  live stats
└───────────────────────────┴──────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same three-part layout everywhere: a &lt;strong&gt;toolbar&lt;/strong&gt; (changing &lt;em&gt;any&lt;/em&gt; option re-runs instantly — you never press Convert twice), an &lt;strong&gt;input card&lt;/strong&gt; (Paste / Copy / Clear, plus a count pill on some tools), and an &lt;strong&gt;output card&lt;/strong&gt; (Swap-to-input / Download / Copy / Clear). Every textarea has a live &lt;code&gt;chars · words · lines&lt;/code&gt; footer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tool 1 — FAQ → Accordion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://yourimageshare.com/ib/lbmGcd76Cw" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fyourimageshare.com%2Fib%2FlbmGcd76Cw.png" alt="FAQ2Accordion" width="800" height="643"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Plain Q/A list → WPBakery Page Builder shortcodes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A line is a &lt;strong&gt;question&lt;/strong&gt; if it ends with &lt;code&gt;?&lt;/code&gt; &lt;em&gt;or&lt;/em&gt; starts with &lt;code&gt;Q:&lt;/code&gt; / &lt;code&gt;Q.&lt;/code&gt; / &lt;code&gt;Q)&lt;/code&gt; / &lt;code&gt;Q-&lt;/code&gt; (case-insensitive). Every non-blank line after it, until the next question, becomes the &lt;strong&gt;answer&lt;/strong&gt; — optional &lt;code&gt;A:&lt;/code&gt; prefixes and leading list numbers (&lt;code&gt;1.&lt;/code&gt;, &lt;code&gt;2)&lt;/code&gt;) are stripped, blank lines ignored, so double-spaced documents work as-is. Press &lt;strong&gt;✨ Sample&lt;/strong&gt; to load a working example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What is your return policy?
You can return any unused item within 30 days for a full refund.

Q: Do you ship internationally?
A: Yes — over 40 countries, 5–10 business days.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[vc_tta_accordion]
[vc_tta_section title="What is your return policy?" tab_id="what-is-your-return-policy"][vc_column_text]You can return any unused item within 30 days for a full refund.[/vc_column_text][/vc_tta_section]
[/vc_tta_accordion]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shortcode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vc_tta_accordion&lt;/code&gt; (default), &lt;code&gt;vc_tta_tabs&lt;/code&gt; or &lt;code&gt;vc_tta_tour&lt;/code&gt;. Inner sections are identical, so you can switch layout without redoing content.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single line output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On by default — WPBakery's classic editor injects stray &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; tags into multi-line shortcodes. Turn off for readable, indented output.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wrap answer in &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;For themes whose accordion body has no default paragraph spacing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tab_id prefix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;faq&lt;/code&gt; → &lt;code&gt;faq-what-is-your-return-policy&lt;/code&gt;. &lt;strong&gt;Essential with two accordions on one page&lt;/strong&gt; — duplicate &lt;code&gt;tab_id&lt;/code&gt;s break open/close behaviour.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;tab_id&lt;/code&gt; generation:&lt;/strong&gt; accent-folded (&lt;code&gt;Héllo&lt;/code&gt; → &lt;code&gt;Hello&lt;/code&gt;), depunctuated, lowercased, spaces → hyphens, with a numeric suffix (&lt;code&gt;-2&lt;/code&gt;, &lt;code&gt;-3&lt;/code&gt;) on collisions. Double quotes in questions are escaped to &lt;code&gt;&amp;amp;quot;&lt;/code&gt; so they can't break out of &lt;code&gt;title=""&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The input pill shows how many Q/A pairs were found. &lt;strong&gt;&lt;code&gt;0 items&lt;/code&gt; means your questions don't end in &lt;code&gt;?&lt;/code&gt; or start with &lt;code&gt;Q:&lt;/code&gt;.&lt;/strong&gt; Questions with no answer beneath them are discarded.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Tool 2 — HTML Cleaner
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://yourimageshare.com/ib/4B5ywKKLkd" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fyourimageshare.com%2Fib%2F4B5ywKKLkd.png" alt="HTML Cleaner" width="800" height="642"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Strips the junk out of HTML pasted from Word, Google Docs, Outlook or another CMS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Independent toggles, applied in this &lt;strong&gt;fixed order&lt;/strong&gt; — order matters, e.g. removing attributes after stripping all tags is pointless.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Smart quotes → plain&lt;/strong&gt; &lt;em&gt;(off)&lt;/em&gt; — curly quotes → straight, en/em dashes → &lt;code&gt;-&lt;/code&gt;/&lt;code&gt;--&lt;/code&gt;, &lt;code&gt;…&lt;/code&gt; → &lt;code&gt;...&lt;/code&gt;. For CSV imports, older PHP templates, some email clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove &lt;code&gt;&amp;amp;nbsp;&lt;/code&gt;&lt;/strong&gt; &lt;em&gt;(on)&lt;/em&gt; — both the entity and the literal U+00A0. The single biggest source of "why is there a weird gap in my paragraph".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove style attrs&lt;/strong&gt; &lt;em&gt;(on)&lt;/em&gt; + &lt;strong&gt;Remove class/id&lt;/strong&gt; &lt;em&gt;(off)&lt;/em&gt; — one pass via &lt;strong&gt;&lt;code&gt;DOMParser&lt;/code&gt;, not regex&lt;/strong&gt;, so attributes inside comments or strings can't be mangled and malformed markup is normalised rather than corrupted. Class/id removal is aggressive, hence off by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags&lt;/strong&gt; &lt;em&gt;(on)&lt;/em&gt; — drops opening tags, converts &lt;code&gt;&amp;lt;/p&amp;gt;&lt;/code&gt; to a newline so paragraph breaks survive. For fields that add their own wrapping (&lt;code&gt;wpautop&lt;/code&gt;, most page builders).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strip ALL tags&lt;/strong&gt; &lt;em&gt;(off)&lt;/em&gt; — nuclear option, leaves plain text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop empty lines&lt;/strong&gt; &lt;em&gt;(on)&lt;/em&gt; — cleans up after the steps above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single line&lt;/strong&gt; &lt;em&gt;(off)&lt;/em&gt; — collapses whitespace runs, for shortcode attributes and JSON fields.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Presets:&lt;/strong&gt; Word/Docs cleanup = style + &lt;code&gt;&amp;amp;nbsp;&lt;/code&gt; + empty lines · Plain text = strip all + smart quotes + empty lines · Page-builder prep = &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; + style + class/id · One-line value = strip all + single line&lt;/p&gt;


&lt;h2&gt;
  
  
  Tool 3 — Link Tool
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://yourimageshare.com/ib/KdTiJSV9Tm" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fyourimageshare.com%2Fib%2FKdTiJSV9Tm.png" alt="Link Tool" width="800" height="641"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Applies classes, &lt;code&gt;target&lt;/code&gt; and &lt;code&gt;rel&lt;/code&gt; to every &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; in a block of HTML at once.&lt;/strong&gt; Also DOM-parsed and re-serialised, so it can never emit a broken tag.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Add class&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Space-separated, e.g. &lt;code&gt;btn btn-primary&lt;/code&gt;. Added via &lt;code&gt;classList.add()&lt;/code&gt; — duplicates impossible, existing classes preserved.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Replace existing classes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wipes &lt;code&gt;class&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; adding yours. For re-skinning links carrying an old theme's classes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;target="_blank"&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Added to every matched link.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;noopener noreferrer&lt;/code&gt; (best practice for &lt;code&gt;_blank&lt;/code&gt;) · &lt;code&gt;nofollow noopener&lt;/code&gt; (paid/untrusted) · &lt;code&gt;nofollow sponsored noopener&lt;/code&gt; (Google's sponsored markup) · &lt;code&gt;ugc noopener&lt;/code&gt; · &lt;code&gt;— keep —&lt;/code&gt; leaves existing values alone.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External only&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limits changes to &lt;code&gt;href&lt;/code&gt;s starting &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;domain field&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;With &lt;em&gt;External only&lt;/em&gt;: links containing this domain count as internal and are skipped. Enter bare, e.g. &lt;code&gt;mysite.com&lt;/code&gt; — leading &lt;code&gt;www.&lt;/code&gt; stripped automatically.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pill reads &lt;code&gt;12 links · 4 updated&lt;/code&gt;. That second number is the sanity check — &lt;code&gt;0 updated&lt;/code&gt; with External-only on means your links are relative paths (&lt;code&gt;/about&lt;/code&gt;), or your domain filter is matching everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical run:&lt;/strong&gt; paste a legacy post, tick External only, enter your domain, pick &lt;code&gt;nofollow sponsored noopener&lt;/code&gt;, tick &lt;code&gt;target="_blank"&lt;/code&gt;, Apply, copy back. Thirty links fixed in five seconds.&lt;/p&gt;


&lt;h2&gt;
  
  
  Tool 4 — Slug Maker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://yourimageshare.com/ib/Hz2yNz4QBt" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fyourimageshare.com%2Fib%2FHz2yNz4QBt.png" alt="Slug Maker" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Titles → clean, URL-safe slugs.&lt;/strong&gt; NFD-normalises and strips combining marks (&lt;code&gt;Héllo Wörld&lt;/code&gt; → &lt;code&gt;Hello World&lt;/code&gt;, not &lt;code&gt;hll-wrld&lt;/code&gt;), removes HTML entities, removes anything that isn't a word character / whitespace / hyphen, collapses separator runs, trims the ends.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Separator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-&lt;/code&gt; (SEO standard) or &lt;code&gt;_&lt;/code&gt; (file names, some CMS taxonomies).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lowercase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On by default. Off preserves casing for case-sensitive asset paths.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;One slug per line&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Batch mode. Off = whole input squashed into one slug.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max length&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; = unlimited. Otherwise truncates &lt;em&gt;then&lt;/em&gt; trims the trailing separator, so you never get &lt;code&gt;my-great-post-&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Héllo Wörld — My Great Post! (2024)&lt;/code&gt; → &lt;code&gt;hello-world-my-great-post-2024&lt;/code&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Tool 5 — Counter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://yourimageshare.com/ib/DLBMErNbDJ" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fyourimageshare.com%2Fib%2FDLBMErNbDJ.png" alt="Counter" width="800" height="646"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Live text metrics plus SEO length guides&lt;/strong&gt; — one full-width input with a stats dashboard beneath.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Characters&lt;/strong&gt; — including spaces and newlines · &lt;strong&gt;No spaces&lt;/strong&gt; — all whitespace removed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chars + offset&lt;/strong&gt; — count plus a configurable &lt;strong&gt;Offset&lt;/strong&gt; (default &lt;code&gt;22&lt;/code&gt;). For when a template appends a fixed suffix (site name in a title tag, tracking string, prefix code) and you need to budget for it while writing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Words&lt;/strong&gt; — whitespace-delimited tokens · &lt;strong&gt;Lines&lt;/strong&gt; — empty box reports &lt;code&gt;0&lt;/code&gt;, not &lt;code&gt;1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentences&lt;/strong&gt; — runs ending &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt; or &lt;code&gt;?&lt;/code&gt;. Approximate; &lt;code&gt;e.g.&lt;/code&gt; inflates it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read time&lt;/strong&gt; — 200 wpm, seconds under a minute, otherwise &lt;code&gt;Xm Ys&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two progress bars track the conventional truncation limits — &lt;strong&gt;SEO title 60&lt;/strong&gt; and &lt;strong&gt;meta description 160&lt;/strong&gt;. Fill is green within budget, red the moment you exceed it, capped at 100% width so long strings don't blow out the layout. Both read the &lt;em&gt;same&lt;/em&gt; textarea, so check title and description one at a time.&lt;/p&gt;


&lt;h2&gt;
  
  
  Shared controls
&lt;/h2&gt;

&lt;p&gt;Every control is a real &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; — focusable, keyboard-activatable, driven by one delegated click handler. No dead &lt;code&gt;href&lt;/code&gt;s.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Button&lt;/th&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;📋 Paste&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reads the clipboard and re-runs the tool. Needs a secure context; if blocked, focuses the field and toasts "use Ctrl+V".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;⧉ Copy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Async Clipboard API with a hidden-textarea &lt;code&gt;execCommand&lt;/code&gt; fallback, so it works on &lt;code&gt;file://&lt;/code&gt; too. Flashes &lt;strong&gt;✓ Copied&lt;/strong&gt; for 1.2 s; warns on an empty field rather than doing nothing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;✕ Clear&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Empties, re-runs, refocuses the textarea.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;⇄ To input&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sends output back to input — chain operations or re-run with different settings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;⭳ Save&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Downloads &lt;code&gt;.txt&lt;/code&gt; / &lt;code&gt;.html&lt;/code&gt; via a Blob URL. Nothing touches a server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;✨ Sample&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(FAQ) Loads demo content.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;▶ Convert / Clean / Apply / Slugify&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Forces a run. Rarely needed — useful after a paste that didn't fire an &lt;code&gt;input&lt;/code&gt; event.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🌙 Theme&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dark/light, stored in &lt;code&gt;localStorage&lt;/code&gt; as &lt;code&gt;jstools-theme&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Toasts appear bottom-right for 2.2 s: success (green), warn (amber), error (red), info (indigo).&lt;/p&gt;


&lt;h2&gt;
  
  
  Internals
&lt;/h2&gt;

&lt;p&gt;~250 lines of JavaScript held together by four conventions. Learn these and you can change anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The &lt;code&gt;RUN&lt;/code&gt; registry&lt;/strong&gt; — every tool is a plain function on one object. Nothing else knows what a tool &lt;em&gt;does&lt;/em&gt;; callers just invoke &lt;code&gt;RUN[name]()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;faq&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* read inputs, write output */&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Declarative buttons (&lt;code&gt;data-act&lt;/code&gt;)&lt;/strong&gt; — exactly &lt;strong&gt;one&lt;/strong&gt; click listener, on &lt;code&gt;document&lt;/code&gt;, reading attributes off whatever was clicked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-act=&lt;/span&gt;&lt;span class="s"&gt;"copy"&lt;/span&gt;     &lt;span class="na"&gt;data-target=&lt;/span&gt;&lt;span class="s"&gt;"faqOut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⧉ Copy&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-act=&lt;/span&gt;&lt;span class="s"&gt;"swap"&lt;/span&gt;     &lt;span class="na"&gt;data-from=&lt;/span&gt;&lt;span class="s"&gt;"faqOut"&lt;/span&gt; &lt;span class="na"&gt;data-to=&lt;/span&gt;&lt;span class="s"&gt;"faqIn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⇄&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-act=&lt;/span&gt;&lt;span class="s"&gt;"download"&lt;/span&gt; &lt;span class="na"&gt;data-target=&lt;/span&gt;&lt;span class="s"&gt;"faqOut"&lt;/span&gt; &lt;span class="na"&gt;data-file=&lt;/span&gt;&lt;span class="s"&gt;"accordion.txt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⭳&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-act=&lt;/span&gt;&lt;span class="s"&gt;"run"&lt;/span&gt;      &lt;span class="na"&gt;data-run=&lt;/span&gt;&lt;span class="s"&gt;"faq"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;▶ Convert&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Event delegation means buttons added later work automatically, and a typo in one can't break the others — the core failure mode of the original inline-&lt;code&gt;onclick&lt;/code&gt; version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Live conversion (&lt;code&gt;data-live&lt;/code&gt;)&lt;/strong&gt; — &lt;code&gt;data-live="faq"&lt;/code&gt; re-runs &lt;code&gt;RUN.faq&lt;/code&gt; 150 ms after you stop typing (debounced, so a 5,000-word paste doesn't re-parse per keystroke). Any &lt;code&gt;input&lt;/code&gt; or &lt;code&gt;select&lt;/code&gt; inside a &lt;code&gt;.toolbar&lt;/code&gt; also re-runs its panel's tool on change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Stats (&lt;code&gt;data-stats-for&lt;/code&gt;)&lt;/strong&gt; — &lt;code&gt;&amp;lt;span data-stats-for="faqIn"&amp;gt;&lt;/code&gt; is auto-populated with &lt;code&gt;#faqIn&lt;/code&gt;'s char/word/line count. Add the span, get the stats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plus:&lt;/strong&gt; &lt;strong&gt;no dependencies&lt;/strong&gt; — jQuery, Bootstrap, toastr and CKEditor are gone (Bootstrap's grid → ~40 lines of CSS Grid, toastr → a 6-line &lt;code&gt;toast()&lt;/code&gt;, jQuery → &lt;code&gt;$&lt;/code&gt;/&lt;code&gt;$$&lt;/code&gt; helpers). &lt;strong&gt;DOM parsing over regex&lt;/strong&gt; — regex-based HTML editing is exactly what produces the mangled markup these tools exist to fix. &lt;strong&gt;CSS custom properties&lt;/strong&gt; for theming.&lt;/p&gt;




&lt;h2&gt;
  
  
  Add your own tool
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Tab button&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt; &lt;span class="na"&gt;data-tab=&lt;/span&gt;&lt;span class="s"&gt;"case"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🔠 Case Converter&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Panel&lt;/strong&gt; — id must be &lt;code&gt;tab-&lt;/code&gt; + the &lt;code&gt;data-tab&lt;/code&gt; value. Copy an existing panel's structure; the only wiring that matters is &lt;code&gt;data-live&lt;/code&gt; on the input, &lt;code&gt;data-stats-for&lt;/code&gt; in the footers, and matching ids:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"panel"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab-case"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"toolbar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"field"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Style
      &lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"cs_mode"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"upper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;UPPERCASE&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Title Case&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btns"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-left:auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn primary"&lt;/span&gt; &lt;span class="na"&gt;data-act=&lt;/span&gt;&lt;span class="s"&gt;"run"&lt;/span&gt; &lt;span class="na"&gt;data-run=&lt;/span&gt;&lt;span class="s"&gt;"case"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;▶ Convert&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- input card --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"caseIn"&lt;/span&gt;  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"editor"&lt;/span&gt; &lt;span class="na"&gt;data-live=&lt;/span&gt;&lt;span class="s"&gt;"case"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-stats-for=&lt;/span&gt;&lt;span class="s"&gt;"caseIn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0 chars&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- output card --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"caseOut"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"editor"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;data-stats-for=&lt;/span&gt;&lt;span class="s"&gt;"caseOut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0 chars&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Register the function&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#caseIn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#caseOut&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#cs_mode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\w\S&lt;/span&gt;&lt;span class="sr"&gt;*/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nf"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#caseOut&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. There is no step 4.&lt;/strong&gt; Tabs, buttons, live typing, stats and toasts all pick it up from the markup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ The &lt;code&gt;data-live&lt;/code&gt; / &lt;code&gt;data-stats-for&lt;/code&gt; wiring runs once at load. Inject a panel at runtime and you must re-run the two &lt;code&gt;$$(...)&lt;/code&gt; setup loops.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Theming
&lt;/h2&gt;

&lt;p&gt;Colours are CSS variables declared twice — &lt;code&gt;:root&lt;/code&gt; (dark) and &lt;code&gt;html[data-theme="light"]&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#0e1320&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--bg2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#141b2b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#19212f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#2a3446&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--txt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#e7edf7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#93a1b8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--accent2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#22d3ee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#22c55e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--warn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#f59e0b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#ef4444&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To rebrand, change &lt;code&gt;--accent&lt;/code&gt; / &lt;code&gt;--accent2&lt;/code&gt; and the &lt;code&gt;.app-header&lt;/code&gt; gradient. For a light-only build, set &lt;code&gt;data-theme="light"&lt;/code&gt; on &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; and delete the toggle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Browser support &amp;amp; clipboard caveats
&lt;/h2&gt;

&lt;p&gt;Current Chrome, Edge, Firefox and Safari. Uses &lt;code&gt;DOMParser&lt;/code&gt;, &lt;code&gt;classList&lt;/code&gt; and template literals — ES2019, no transpilation, no IE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copy works everywhere:&lt;/strong&gt; &lt;code&gt;navigator.clipboard.writeText()&lt;/code&gt; first, hidden textarea + &lt;code&gt;document.execCommand('copy')&lt;/code&gt; as fallback, explicit "press Ctrl+C" toast if both fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paste is conditional&lt;/strong&gt; — &lt;code&gt;navigator.clipboard.readText()&lt;/code&gt; is far more restricted:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;https://&lt;/code&gt; in Chrome/Edge&lt;/td&gt;
&lt;td&gt;✅ Works (may prompt once)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http://localhost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;file://&lt;/code&gt; (double-clicked)&lt;/td&gt;
&lt;td&gt;❌ Insecure context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;❌ Blocked for regular pages by design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safari&lt;/td&gt;
&lt;td&gt;⚠️ Genuine user gesture only; often still blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On failure the app focuses the textarea and toasts &lt;em&gt;"Browser blocked paste — use Ctrl+V"&lt;/em&gt;. Manual pasting always works and fires the same live conversion, so nothing is lost. &lt;strong&gt;Host over HTTPS if you want the Paste buttons.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Counter's two SEO bars read one field&lt;/strong&gt; — check title and description separately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentence counting is naïve&lt;/strong&gt; — &lt;code&gt;e.g.&lt;/code&gt;, &lt;code&gt;Dr.&lt;/code&gt; and decimals each count as a sentence end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FAQ parsing needs a marker&lt;/strong&gt; — statement headings ("Shipping information") get absorbed into the previous answer. Prefix with &lt;code&gt;Q:&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DOMParser&lt;/code&gt; normalises markup&lt;/strong&gt; — self-closing tags, attribute quoting and tag casing may change. Valid HTML, just not byte-identical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing persists&lt;/strong&gt; except the theme. Refresh clears every textarea — intentional, but worth knowing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No undo&lt;/strong&gt; — &lt;strong&gt;⇄ To input&lt;/strong&gt; overwrites without confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code smell:&lt;/strong&gt; &lt;code&gt;RUN.slug&lt;/code&gt;'s &lt;code&gt;if(!$('#sl_lower').checked === false)&lt;/code&gt; works but should be &lt;code&gt;if($('#sl_lower').checked)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Persist textareas in &lt;code&gt;localStorage&lt;/code&gt; (+ Clear-all)&lt;/li&gt;
&lt;li&gt;[ ] Separate title/description fields in the Counter&lt;/li&gt;
&lt;li&gt;[ ] Markdown ⇄ HTML tab&lt;/li&gt;
&lt;li&gt;[ ] Find &amp;amp; replace with regex and match count&lt;/li&gt;
&lt;li&gt;[ ] Gutenberg block output alongside WPBakery&lt;/li&gt;
&lt;li&gt;[ ] Keyboard shortcuts (&lt;code&gt;Ctrl+1..5&lt;/code&gt;, &lt;code&gt;Ctrl+Enter&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] URL-hash routing (&lt;code&gt;#links&lt;/code&gt; deep-links a tool)&lt;/li&gt;
&lt;li&gt;[ ] Export/import option presets as JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/TheOtherAlexx/JSTools" rel="noopener noreferrer"&gt;JS Tools on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>javascript</category>
      <category>tools</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
