Source Code

<div class="sss-phone">
  <div class="sss-screen">
    <div class="sss-status"><span>9:41</span><span class="sss-batt"><i></i></span></div>

    <header class="sss-head">
      <b>Settings</b>
    </header>

    <div class="sss-search-wrap">
      <div class="sss-search">
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
        <input type="text" id="sssInput" placeholder="Search settings">
        <button id="sssClear" hidden aria-label="Clear search">&times;</button>
      </div>
    </div>

    <div class="sss-list" id="sssList"></div>
    <p class="sss-empty" id="sssEmpty" hidden>No settings found for "<span id="sssEmptyQuery"></span>"</p>
  </div>
</div>

Mobile Settings Screen with Live Search — Free Snippet

Mobile Settings Screen with Live Search · Mobile · Plain HTML, CSS & JS · Live preview

What's included

Features

Single settings array drives search filtering, group headers, and toggle state together
Search matches against both a row's title and its subtitle text
Matched substrings are wrapped in a highlight mark, not just present in a filtered list
Groups with zero matching rows disappear automatically as a byproduct of filtering
Toggle switches keep working correctly while a search filter is active
Explicit "no settings found" empty state echoes back the actual typed query
Clear button resets the search and returns focus to the input
Zero dependencies, vanilla JavaScript only

About this UI Snippet

Mobile Settings Search Screen — Instant Filter, Highlighting & Working Toggles

Screenshot of the Mobile Settings Screen with Live Search snippet rendered live

Once a settings screen grows past a couple dozen rows, scrolling through grouped sections to find one specific option becomes slower than just knowing its name and typing it. This snippet adds a real search field above a grouped settings list — filtering happens on every keystroke, matched substrings are visually highlighted, and every toggle switch in the filtered results still actually works, not just the ones visible before searching.

One settings array drives search, grouping, and toggling

Every setting is one object in a flat settings array with a group, icon, title, sub, and a type of either 'nav' or 'toggle' (with an on boolean for toggles). render(query) filters this single array, then re-groups the filtered results by group on the fly — there is no separate pre-grouped data structure to keep in sync with the flat searchable list.

Filtering matches both the title and the subtitle

The filter predicate checks item.title and item.sub against the lowercased query, so searching "2FA" finds "Password and security" (whose sub-line mentions 2FA) even though the word doesn't appear in the row's title. This mirrors how a real settings search should behave — users often remember what a setting does before they remember what it's officially called.

Matches are highlighted, not just present

highlight(text, query) finds the first case-insensitive match of the query inside a string and wraps it in a <mark> tag. This gives the same at-a-glance scanning benefit as a real search UI (like a browser's find-in-page) instead of a plain filtered list, where a user might otherwise need to re-read every row to see why it matched.

Groups appear and disappear based on what's left

Since render() rebuilds the group structure from the filtered array on every keystroke, a group with zero matching rows simply doesn't get a <p class="sss-group-label"> rendered at all — group headers are a byproduct of filtering, not separately hidden or shown.

Toggles keep working after filtering

Clicking a toggle button looks the underlying setting object up from settings by its title via toggleSetting(), flips its on boolean, and re-runs render(input.value) with the current search term still applied — so switching a toggle while a search is active does not clear the search or lose the user's place in the filtered list.

An explicit no-results state

When the filtered array is empty, the list is cleared and a "No settings found for…" message appears with the actual typed query echoed back, rather than leaving a blank scroll area that could be mistaken for a loading or broken state.

Wiring it to real settings persistence

Replace the in-memory on boolean flips inside toggleSetting() with an API call (or a local settings store write) that actually persists the preference, and load the initial settings array's toggle states from the user's saved preferences rather than the hardcoded demo defaults.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than tracing the filter-and-regroup logic by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how render() derives both the highlighted matched text and the regrouped section structure from one flat settings array and the current search query on every keystroke. The same assistant can help you optimize it, for instance asking whether the search should debounce its filtering for a very large settings list instead of re-rendering the whole grouped structure on every keystroke. It is also useful for extending the screen: ask it to persist toggle changes to a real backend or local settings store, add recent or suggested searches above the results, or support multi-word queries that must all match rather than a single substring. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a mobile "settings screen with live search" in plain HTML, CSS, and JavaScript, framed inside a CSS phone mockup, no library.

Requirements:
- A single JavaScript array of setting objects, each with a group name, an icon, a title, a subtitle, and a type of either a navigation row (chevron) or a toggle row (on/off switch with its own boolean state) — this one array is the only source of truth for the list, its grouping, and each toggle's state.
- A search input above the list that filters the array on every keystroke against both each item's title and its subtitle text (case-insensitive substring match), and re-groups only the matching items under their original group headers, with groups that have zero matches simply not appearing.
- The portion of each visible row's title and subtitle that matches the current search query must be visually highlighted (wrapped in a mark element), not just present in the filtered list.
- Toggle switches within the filtered results must remain fully functional: clicking one flips its underlying boolean state in the shared array and re-renders the list with the current search term still applied, without clearing the search or losing the filtered view.
- A clear (X) button inside the search field must appear only when there is text entered, and clicking it must reset the search, show the full unfiltered list again, and refocus the input.
- When no settings match the current query, show an explicit empty-state message that includes the actual typed search term, rather than leaving a blank list.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA grouped settings list renders with an empty search field above it.
  2. 2
    Type in the search fieldThe list filters instantly on every keystroke, matching against both each row's title and its subtitle.
  3. 3
    Watch the matched text highlightThe matching substring inside each visible row is wrapped in a yellow highlight.
  4. 4
    Tap a toggle switchIt flips on/off immediately, even while a search filter is currently applied.
  5. 5
    Clear the searchTap the X button inside the search field to reset the list and refocus the input.
  6. 6
    Wire it to real persistenceReplace the in-memory toggle state with an API call or local settings store write inside toggleSetting().

Real-world uses

Common Use Cases

Apps with large, deeply grouped settings
Any app whose settings screen has grown past a handful of toggles benefits from a real search instead of forcing users to scroll and hunt.
Admin dashboards and internal tools
Pair with a Mobile Appearance Settings Screen for a deeper look at one specific settings group this search screen would link into.
Accessibility and preference-heavy apps
Surfacing subtitle text in the search match (not just titles) helps users find settings by what they do, not just their exact label.
Teaching live-filter and highlight patterns
A compact reference for filtering, re-grouping, and highlighting matched text from one flat array on every keystroke.
Related: Mobile Settings Screen
See the Mobile Settings Screen for the non-search grouped-settings baseline this screen extends.
Related: Mobile Search Screen
See the Mobile Search Screen for a related live-search interaction pattern worth comparing against this one.

Got questions?

Frequently Asked Questions

Both a row's title and its subtitle text, compared case-insensitively. This means a query can match a row even if the search term only appears in the descriptive subtitle rather than the row's displayed title.

The highlight() function finds the first case-insensitive occurrence of the search query within a string and wraps just that substring in a <mark> tag, leaving the rest of the text unwrapped so only the actual match is visually highlighted.

render() rebuilds the grouped structure from the filtered array on every keystroke. A group with zero rows remaining after filtering simply never gets a group label rendered, since group headers are derived from whatever items are currently visible rather than toggled separately.

Yes — clicking a toggle looks up the underlying setting object by its title, flips its on/off boolean, and re-runs render() with the current search text still applied, so the search filter and scroll context are preserved after toggling.

The list is cleared and an explicit "No settings found" message appears with the actual typed query echoed back, so an empty result reads as a deliberate no-matches state rather than a blank or broken screen.

Yes. Keep settings as state, derive a filtered and grouped structure from it and the search query in a computed/useMemo value, and render the highlight by splitting the matched substring into separate text and <mark> nodes.