⏳ This skill is pending AI review.

Scores will appear once the review pipeline completes.

version unknown

redesign-screen

@mobbin⭐ 64 stars

Redesign any screen from patterns real apps ship. Share a screenshot, your code or a design file, and the skill finds how apps on Mobbin handle the same screen. Explore a few directions or fix specific problems, and see the evidence behind each change. You get a before and after image and a short report. The redesign stays in your own design system, and anything that already works is kept. Use whenever the user points at a screen, section, or component they have, or at a reference, a Mobbin result, one or more Mobbin collections or the screens they saved, and asks to redesign, improve, restyle, or rebuild it, or to see a few directions for it.

Choose how to use this skill

You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.

1. Native installer

This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.

Do not guess an installer command or replace an existing version without reviewing the diff.

2. Complete package recommended

Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.

No complete ProSkills package is published for this listing yet.

3. Prompt-only

Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.

Need only the instruction file?

Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.

No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.

—/10

// RATINGS

⭐GitHub Stars
⭐⭐ 64 on GitHubGitHub ↗

Growing

🟢ProSkills Score
—
📍

Not yet listed on ClawHub or SkillsMP

// README

Mobbin Skills

A collection of skills for designing and building UI with Mobbin, the industry standard in UI & UX design references for mobile apps, web apps, and websites

Installing

Use the native plugin where supported to install both the Mobbin skills and the Mobbin MCP server. Agents that only support the Agent Skills standard can install the skills separately.

On first use your agent opens a browser to sign in to Mobbin. You need a Pro, Team, or Enterprise plan.

Codex

codex plugin marketplace add mobbin/skills
codex plugin add mobbin@mobbin

Claude Code

claude plugin marketplace add mobbin/skills
claude plugin install mobbin@mobbin

Or from inside a Claude Code session:

/plugin marketplace add mobbin/skills
/plugin install mobbin@mobbin

Antigravity CLI

agy plugin install https://github.com/mobbin/skills

Kimi Code CLI

/plugins install https://github.com/mobbin/skills

Run /reload afterwards to apply the plugin.

Gemini CLI

gemini extensions install https://github.com/mobbin/skills

Then run /mcp auth mobbin inside Gemini CLI to sign in.

npx skills

Install using the npx skills CLI:

npx skills add mobbin/skills

This installs only the skills. Add the MCP server to your agent's MCP config yourself:

{
  "mcpServers": {
    "mobbin": {
      "url": "https://api.mobbin.com/mcp"
    }
  }
}

Skills

Skills are contextual and auto-loaded based on your conversation. When a request matches a skill's triggers, the agent loads and applies the relevant skill.

SkillUseful for
best-practicesWhat Mobbin and the Mobbin MCP can and can't do, and how to use the Mobbin skills together. Read first when the user asks about Mobbin or starts a design task Mobbin could help with
searchSearching Mobbin well: choosing between screens, flows and sections, picking the mode, writing a query, pulling more results, and recovering when a search falls short
redesign-screenRedesigning, improving, restyling or rebuilding a screen, section or component from a screenshot, code or a design file, grounded in how real apps on Mobbin handle the same screen or in the screens you saved to Mobbin collections
plain-writingWriting rules for the text a Mobbin skill hands to a person, such as a redesign report. Loaded by other Mobbin skills, not meant for writing outside them

Terms of Service · Privacy Policy · Help Center

// HOW IT'S BUILT

// FILES

17 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.

  • skills/redesign-screen/SKILL.md20 KB3f5486100c8aupstream ↗
    ---
    name: redesign-screen
    description: Redesign any screen from patterns real apps ship. Share a screenshot, your code or a design file, and the skill finds how apps on Mobbin handle the same screen. Explore a few directions or fix specific problems, and see the evidence behind each change. You get a before and after image and a short report. The redesign stays in your own design system, and anything that already works is kept. Use whenever the user points at a screen, section, or component they have, or at a reference, a Mobbin result, one or more Mobbin collections or the screens they saved, and asks to redesign, improve, restyle, or rebuild it, or to see a few directions for it.
    ---
    
    # Redesign a screen from a reference
    
    Work as a design lead whose every decision is backed by a real product. Someone has a screen that exists and wants a better version of it. The references decide what to build and are the evidence for it. The user's own design decides how it looks. Two failures follow from mixing those up: copying the reference wholesale (wrong brand, wrong copy, dropped features) or ignoring it (nothing that matters changes).
    
    ## Workflow
    
    1. **Scope the target.** Write the job, walk the path, and settle target, problems and what must survive, all before the first search.
    2. **Freeze the baseline.** Write down what must not change before changing anything.
    3. **Search and lock references**, or read the collection the user pointed at. Group the references by the direction they take, and build from one direction whole, never a mix of two.
    4. **Plan the moves.** One move per problem, each backed by screens that show its decision.
    5. **Apply** the moves in the source's own form.
    6. **Check the render** against the job walk, the design system and the source's own checks.
    7. **Render the before and after image.**
    8. **Report**, explaining each change against the screens that informed it.
    
    Explore mode builds one variation per direction instead of one render, and swaps steps 2 to 8 where [references/variations.md](references/variations.md) says.
    
    ## Reference files
    
    Read each file in full at the step that names it, not before. Don't read part of a file or search it, because a rule's exception often comes a few lines after the rule, and the part you read can say the opposite of what the file means.
    
    | File | Read at |
    |------|---------|
    | [references/scoping-round.md](references/scoping-round.md) | step 1, before asking anything |
    | [references/scope-and-preserve.md](references/scope-and-preserve.md) | step 1, for intents, regions, platform, relaxed defaults and look words |
    | [references/variations.md](references/variations.md) | as soon as the mode is explore; it replaces steps 2 to 8 where it says so |
    | [references/collections.md](references/collections.md) | step 3, when the user named one or more collections or pointed at their saved items |
    | [references/taste.md](references/taste.md) | steps 4 to 6, for how every part is built |
    | [references/before-after-board.md](references/before-after-board.md) | step 7 |
    | [references/report.md](references/report.md) | step 8 |
    | [references/reference-board.md](references/reference-board.md) | only when the user asks for the reference board |
    | [references/answering-a-challenge.md](references/answering-a-challenge.md) | only when the user asks why |
    
    ## Ground rules
    
    Every claim the user reads has evidence. Only three kinds count:
    
    1. **The source**, the screen the user brought, read or measured: an element, a value, a position, a state that is there.
    2. **The user's words**, quoted or kept in their terms.
    3. **A Mobbin screen you looked at**, named and linked to its `mobbin_url`.
    
    Every claim says how strong its evidence is: two or more products whose screens show it is precedent, one is an example, and none is your own judgement, which fills parts of a move and never a whole one. In the report, the References line under each entry shows it. Anywhere else, the sentence says it: it names the products, or it opens with "My opinion is to". A claim with no evidence behind it is cut, not softened.
    
    What that rules out, in any text the user reads, from the first scoping option to the answer to a why:
    
    - **No Mobbin before step 3.** Until the search has run or the collection has been read, and its images are looked at, nothing is cited, so the scoping round uses only the source and the user's words.
    - **No class of products you did not look at.** No "most apps", "best-in-class", "industry standard" or "the way high-converting paywalls do": name the products, each a screen you looked at.
    - **No research, principle or heuristic from memory.** No "research shows", named law or name-dropped authority, since the skill has no corpus of studies.
    - **The job walk and taste.md are method, not memory.** A fault's reason is the step of the job it blocks, and [references/taste.md](references/taste.md) makes judgement calls without ever being named to the user.
    - **No behavioural outcome.** Never say the run converts, retains, builds trust or reduces friction: a problem names the step it blocks, a move names the decision it makes, and a metric the user asked for stays in their words, never claimed as moved.
    - **No quality adjective without the thing behind it.** "Dated", "cluttered", "modern", "clean" or "premium" appear only in the user's words or beside the part they describe.
    - **Measure in pixels, speak in what is seen.** Sizes, coordinates, hex values and ratios stay in your notes; the user reads what they mean ("the close button is now the platform's minimum size"), numbers on the screen, positions against parts they can see, and no number you did not measure or count.
    - **No claim the image does not show.** A sentence about a move names only a difference a reader can see between BEFORE and AFTER at the image's scale, never what was planned and did not land.
    
    ## Inputs
    
    The user brings a target in whatever form they have it: a codebase the working directory renders, an image, a design file, a live page, or whatever a connected tool exposes. That form is the **source**. Read it with whatever the environment offers, then treat every source alike: a region, elements, visible states, a palette, a type and spacing scale, and the design system those imply. The form decides only how the region is named, where the states come from, and the form the render goes back in.
    
    They may also bring their words (a complaint, a goal, a constraint, a look) and a reference of their own (a screenshot, a Mobbin link, a product name), which sets what the redesign aims at, with Mobbin screens still locked beside it so the result is not a copy of one product. Mobbin supplies the references through `search_screens`, `search_sections` and `search_flows`; when they are not connected, say so in one line and stop, since the skill does not run on memory.
    
    A **collection** the user names, links or points at ("my saved screens", "my checkout and paywall collections", "the screens I saved from Duolingo") is the evidence they chose. It is read with `list_collections` and `get_collection_contents` in place of a search, and the run stays inside it unless the user says otherwise, as [references/collections.md](references/collections.md) sets out.
    
    ## Mode
    
    Two modes, set by `--explore` or `--targeted` in the invocation, or asked as the first scoping question when neither is given ([references/scoping-round.md](references/scoping-round.md)):
    
    - **Explore** (`--explore`): up to three variations, each built whole from one direction, as mockups beside the baseline. Nothing is applied until the user picks one. Read [references/variations.md](references/variations.md) and follow it; it says which steps below it replaces.
    - **Targeted** (`--targeted`): the problems the user ticks, fixed in one design applied to the source, following the steps below as written.
    
    ## Outputs
    
    The render goes back in the form the source came in:
    
    | Source | Render |
    |--------|--------|
    | A codebase | edits to the source |
    | A design file | a new frame beside the original, written with the design tool's own tools, so the baseline survives |
    | A design file the tool can only read | a captured mockup in the product's own palette, type and spacing, said once at scoping |
    | An image | a PNG |
    | Anything else | the nearest of the three, said at scoping |
    
    Check that a frame can be written before promising one, since most design-tool connections only read. In explore mode every source renders as mockups first, and the table applies to the variation the user picks.
    
    The reply leads with the before and after image (step 7) when the render can be captured, then the report and the closing menu (step 8). The reference board and the list of references come only when asked.
    
    ## 1. Scope the target
    
    Read the source first, so every question names real parts of this product.
    
    **Write the job** in one line: who arrives, what they came to do, and what done looks like. "A team admin choosing a plan and paying for it, done when the payment is confirmed." It comes from the source and the user's words, never a reference. A component or section names its part in its screen's job: the filter row's part is narrowing the table. When the job will not fit one line, the first question asks it.
    
    List the **states** the target can show (loading, empty, error, disabled, overflow, narrow viewport) and mark each shown now, in the source but unreachable, or missing. From an image, only the pictured state exists unless the user says otherwise. Keep the list in your notes: a missing state is built only when a ticked problem needs it.
    
    **Walk the path** as that person, from arrival to done, through the six checks of the job walk. Every break is a candidate problem.
    
    1. **The path leads.** The eye lands on the job's first step, one primary control finishes it, and nothing off the path competes, including anything the shell around the region already does.
    2. **Every decision has its information beside it**, not a scroll or a tap away.
    3. **Nothing the job needs is missing.**
    4. **Every label and value is true.** A control does what it says, and an icon without a label is one the platform or the product already teaches (close, share, back, a tab it repeats); an unlabelled icon the design system uses throughout is its style, not a fault. A value shown twice reads the same, and a total matches its lines.
    5. **Every state the target enters** lets the job be done, or says why not.
    6. **The platform's minimums hold**: target size, the smallest text step, contrast from the system's own pairs.
    
    Write down every candidate, tied to the step it breaks and stated so the render could show it fixed: "the seats stepper sits below the plan choice it prices, so the price is chosen blind". "It looks dated" is not a candidate until it names the step it slows. Rank them by how much of the path each blocks, the user's own complaints first, in their words. Step 6 walks the render the same way.
    
    **Then ask** the scoping round, as [references/scoping-round.md](references/scoping-round.md) sets out, leaning on [references/scope-and-preserve.md](references/scope-and-preserve.md) for intents, regions, shared components and platform. Every answer and assumption goes into the **Scope line**.
    
    ## 2. Freeze the baseline
    
    Read the target in full and the design system it uses: the tokens or visible palette, the components it composes from, the primary colour, the spacing and type scale.
    
    Write the **Preserve list**, what must survive, in this order:
    
    1. **The domain.** The product stays the same kind of product; a reference from another domain lends structure only.
    2. **Design system and theme.** Structure changes inside the system, which is never swapped for the reference's.
    3. **Copy and data.** The screen keeps its own words and data. A new label is written in this product's terms, never taken from a reference.
    4. **Everything outside the target.** When the target is one component or section, the rest of the screen does not change. The boundary is named the way the source shows it: a file and symbol in code, a visible edge in an image.
    5. **What the user asked to keep**, from the Preserve question, and any part of the target that already works, named, where changing it would cost more than it gains.
    
    Fix the **problem list**: the ticked problems, each tied to the step it breaks and each getting one move, **five at most** so the report and the image stay readable. Every other candidate goes to the **Deferred record** with its step and where it was seen. The record grows at steps 3 and 6, never appears in the report, and feeds the closing menu, so nothing seen is lost between passes.
    
    ## 3. Search and lock references
    
    **From a collection.** When the user points at their saves, read [references/collections.md](references/collections.md) first. It sets what is read and whether a search may join. Everything else in this step holds, with the collection's screens in place of search results.
    
    **Explore the problem space.** Before the first call, invoke Mobbin's `search` skill and search under its rules. Start with one query per problem, at most two before a second pull, and look at every image. A screen counts only when the decision that answers a problem is visible in it, not in its title. Count products, not captures: twenty captures of one app are one precedent, cited by the capture that shows the decision best. Prefer a matching job over a matching look, and never mix intents; a section screen does not inform a component.
    
    **Identify the directions.** Where the relevant screens disagree on structure (tabs against a chip row, a card that acts against a card that links), sort them into sides by the structural decision each shares.
    
    **Take a stand.** Pick one side for the whole target, not one per problem; a three-way split goes to the side that answers the most problems. Write down its apps, the decision they share, and one sentence on why it fits the job, specific enough that it could not also justify the other side. Take the side whole: its controls, its hierarchy and its scale, in the product's own components and type, and built as [references/taste.md](references/taste.md) sets out. The other side leaves the moves and earns one clause in the report. A break the references expose that the walk missed goes to the Deferred record with the screen that showed it.
    
    **Mark the references** on the taken side, each noted with app, `mobbin_url`, what it does that the baseline does not (the take), and what you will not carry (the leave: light or dark background, accent, typeface, copy, example data):
    
    - **Anchors**: the screens that show the direction best. The redesign is built mainly on these. Usually two to four; one is fine when the search found one.
    - **Pool**: other screens on the side. A move may cite one for a single detail, such as an empty state or a label, and it shapes nothing else.
    - **Dropped**: screens that answer no problem, kept in your notes with a one-line reason and never used.
    
    ## 4. Plan the moves
    
    A **move** is one change to the target that fixes one ticked problem with a decision screens on the taken side show. Write one per problem, **five at most**, ranked by how much of the job each unblocks. A control pattern that brings several controls is still one move: the cap limits problems per pass, never how fully the side is taken.
    
    Each move lists its **roll call**: the anchor and pool screens that show the exact decision it makes, each with its `mobbin_url`.
    
    - **Cite the decision, not the element.** A move that labels a logo row needs a screen with a labelled logo row; a bare logo row does not count.
    - **Cite where displaced things land.** A move that pushes something aside shows where it goes on a cited screen, or the report calls the placement your judgement. A wrapper, row or block the side's screens lack is removed, cited to them, and reported.
    - **No screen, no move.** Judgement fills details a screen does not show (placement, wording) under [references/taste.md](references/taste.md), never a whole move.
    
    Cut or fix a move that:
    
    - adds a second change for a problem another move covers;
    - removes content the baseline had (a plan, link, row, action, badge, label): it stays on the screen, where a cited screen puts it, or where the baseline had it when none shows it;
    - moves content off this screen: it stays this pass, and the closing menu offers the move as a follow-up, citing the screen that puts it elsewhere;
    - does something no product on the taken side does, or keeps the old shape only because the baseline had it;
    - adds a stock detail (a wishlist heart, rating row, badge, hover lift, skeleton, icon on every label, extra micro-copy, gradient overlay) without a named problem and two products on the side that do it.
    
    Then write the **move table**, one row per move: problem, side, roll call, what it adds, what it removes. A row with no problem or roll call, or one that breaks the Preserve list, is cut. Nothing is built that is not a row.
    
    ## 5. Apply
    
    Build the move table in the form Outputs names for the source, changing only the target: never the theme, its tokens or a shared component unless asked. The Preserve list holds whatever the request says, and while building that means:
    
    - **Build only from the product's design system.** Use its existing components, colour tokens, spacing and type scale, and let its theme handle dark mode. Never add a one-off control, a raw colour value, or a new library, typeface or icon set.
    - **Take the reference's structure, not its look.** Never copy its light or dark background, accent colour, typeface, logo or illustration style.
    
    The cited screens decide what each move does; [references/taste.md](references/taste.md) decides how it sits in this product.
    
    Keep interactions real: a control drives real state, not decoration. Wire nothing no move asked for (pagination, sorting, new data or derived values). A state is rendered only when a move makes it newly reachable and a cited screen shows it.
    
    ## 6. Check the render against the job
    
    Before capturing, walk the render as the person the job names, through the six checks of the job walk plus three for the render alone:
    
    - only the states a move made reachable are rendered;
    - nothing outside the target moved;
    - no dealbreaker in [references/taste.md](references/taste.md) is broken.
    
    Only read the render: no new search.
    
    Each check passes or fails. Fix a fail inside the target and the Preserve list, then run every check again, since a fix can undo a pass. Three rounds at most.
    
    - A fail that needs a new move, a new reference or content from outside the target is not fixed: cut the move that caused it, and report it under its problem.
    - A fail still standing after the third round cuts its move the same way.
    - A break no move caused goes to the Deferred record.
    
    Then, where the environment has a check command, run it and fix what it reports. Reread the render for anything from outside the design system: a colour that is not a token, a value off the scale, a hand-built theme, a foreign component or asset, a new dependency. Each is fixed or named in the report.
    
    ## 7. Render the before and after image
    
    When the environment can capture the render, the user sees it before reading about it. Build the board as [references/before-after-board.md](references/before-after-board.md) sets out. The AFTER pane is the applied change captured, never redrawn from memory.
    
    ## 8. Report
    
    Write the report as [references/report.md](references/report.md) sets out, under `plain-writing`.
    
    **On request only:**
    
    - **The reference board**, when the user picks it from the closing menu or asks for the references as an image: build it as [references/reference-board.md](references/reference-board.md) sets out.
    - **A why after the render** ("why the list and not the table?"): reply as [references/answering-a-challenge.md](references/answering-a-challenge.md) sets out. The render is defended, and the stance moves only on a new fact.
    
  • skills/redesign-screen/scripts/embed-images.py3.2 KB6d4d8583769cupstream ↗
    #!/usr/bin/env python3
    """
    Puts a filled board's images inside it, so the file opens on its own.
    
    A board's JSON names its images by path, which only works while the images sit
    beside the file. A host that sends files one at a time, or a user who forwards
    the board, separates them and every pane goes blank. This swaps each image path
    in #board-data for a data: URI of that file and rewrites the board in place.
    
      python3 embed-images.py board.html
    
    Works on board.html, variations.html and reference-board.html. Only the fields
    the templates draw as images are touched: "image", and "before" and "after" when
    they hold a path, so a caption that ends in ".png" stays text. Paths resolve
    from the board's own folder. A value already embedded, or a link, is left as it
    is, so running it twice is safe. A path that names no file, or a file that is
    not an image type below, stops the run with that path named and the board
    unchanged.
    """
    
    import base64
    import json
    import re
    import sys
    from pathlib import Path
    
    DATA_BLOCK = re.compile(
        r'(<script type="application/json" id="board-data">)(.*?)(</script>)', re.DOTALL
    )
    IMAGE_TYPES = {
        ".png": "image/png",
        ".jpg": "image/jpeg",
        ".jpeg": "image/jpeg",
        ".webp": "image/webp",
        ".gif": "image/gif",
        ".avif": "image/avif",
        ".svg": "image/svg+xml",
    }
    IMAGE_FIELDS = {"image", "before", "after"}
    
    
    def main():
        if len(sys.argv) != 2:
            sys.exit("usage: embed-images.py <board.html>")
        board = Path(sys.argv[1]).resolve()
        html = board.read_text(encoding="utf-8")
        block = DATA_BLOCK.search(html)
        if block is None:
            sys.exit(f"{board}: no #board-data block")
    
        data = json.loads(block.group(2))
        embedded, failed = [], []
        data = embed(data, board.parent, embedded, failed)
        if failed:
            sys.exit("Board left unchanged:\n  " + "\n  ".join(failed))
    
        # "</" is escaped so no text field can close the script tag early.
        payload = json.dumps(data, indent=2, ensure_ascii=False).replace("</", "<\\/")
        html = html[: block.start(2)] + "\n" + payload + "\n    " + html[block.end(2) :]
        board.write_text(html, encoding="utf-8")
    
        size = board.stat().st_size / 1024
        print(f"Embedded {len(embedded)} image(s) in {board.name} ({size:.0f} KB)")
    
    
    def embed(value, folder, embedded, failed, field=None):
        """Walks the JSON and swaps each image field's local path for its data: URI."""
        if isinstance(value, dict):
            return {key: embed(item, folder, embedded, failed, key) for key, item in value.items()}
        if isinstance(value, list):
            return [embed(item, folder, embedded, failed, field) for item in value]
        if (
            not isinstance(value, str)
            or field not in IMAGE_FIELDS
            or re.match(r"^(data|https?):", value)
        ):
            return value
        media_type = IMAGE_TYPES.get(Path(value).suffix.lower())
        if media_type is None:
            failed.append(f"Not a supported image type: {value}")
            return value
        path = (folder / value).resolve()
        if not path.is_file():
            failed.append(f"Image not found: {value}")
            return value
        embedded.append(value)
        return f"data:{media_type};base64," + base64.b64encode(path.read_bytes()).decode("ascii")
    
    
    if __name__ == "__main__":
        main()
    
  • skills/redesign-screen/references/answering-a-challenge.md1.6 KB1514854a76a3upstream ↗
    # Answering a challenge after the render
    
    A why arrives in a later turn, after the report: "why the list and not the table?" The run is over, and the pull is to reopen it. This file sets what the reply does instead. Step 8 of the skill points here, so a run that is never challenged does not carry it.
    
    ## Defend the render that exists
    
    The reply follows Ground rules and the report's voice.
    
    1. **Open with the reason**, in one sentence.
    2. **Give the facts behind it**, each once, from your notes: the side taken and its apps, the side not taken and why, and the problem the move answered with its roll call, every app named and linked as in the report. Before you give a rule as a reason, read its whole file again, from the copy of the skill this run loaded, because the rule's exception may come a few lines later.
    3. **Say which reasons still hold.** A reason that rested on a constraint the user has since changed is named as voided in one clause. The same reply decides whether the direction survives without it, and never offers that as a choice.
    
    The side not taken keeps its one clause: no re-argument, no comparison frame, no third variant to settle what the run already settled.
    
    ## What moves the stance
    
    Only a new fact: a constraint the user changed, or something the run demonstrably misread at step 3. Then the reply names the fact and re-renders under the same contract. The question itself is not a new fact, and nor is noticing afterwards that a screen already on the roll call showed something. A why asks for the reasons; it is not a cue to half-concede. No "I think", "I'd push back on myself" or "I could have", and no offer at the end.
    
  • skills/redesign-screen/references/before-after-board.md6.1 KB1224b57a9abdupstream ↗
    # The before and after board
    
    Step 7 shows the user the render before they read about it. This file sets how the board is filled, captured, read back, made to stand alone and delivered. The AFTER pane is the applied change captured, never redrawn from memory.
    
    ## Fill the template
    
    The board is the skill's `assets/board.html`. Copy it next to the two pane images and fill in the JSON block inside it. Change nothing else: the layout, the type and the marks are set there. Rewriting them spends tokens on CSS and loses the type rules that keep the words readable. The data carries:
    
    - **The two panes.** Paths to the BEFORE and AFTER captures, made however the environment allows: screenshots of the running source, exports of the original and the new frame, or an HTML mockup of the target in the source's palette, type and spacing, then captured. Both show the full target at the same scale and crop, and nothing outside the region differs. The board holds those captures as images and never rebuilds the UI itself, so what the user reads about is what was applied.
    - **One entry per move**, numbered in the problem list's order, which is the report's order. A cut move has no entry. Each entry carries:
      - its bold lead-in, one-line explainer and References line from the report, the last as each app's name and `mobbin_url` so the comparison file links to them;
      - the part it touched as a rect on every pane it is on: a removed part on BEFORE only, a new one on AFTER only, a changed one on both.
    - **The theme** the baseline was captured in. A second board in the source's other theme follows only when the source exposes it and the render can be produced in it.
    - **The meta line**, top right beside the stamp: platform and target, the number of changes and of references ("iOS paywall, 2 changes, 3 references").
    - **The mark colour**, `--call` in the template, is the design system's blue. When the product's accent is close to that blue, set `--call` to `var(--warning)`, the system's amber: a mark in the accent reads as part of the render instead of a note on it. The rest of the board is the Mobbin design system and does not change.
    
    ## Rects
    
    Rects are percentages of the pane, so they hold at any capture scale. Each row's crops are cut from the same two panes, so a move needs no screenshot of its own.
    
    A rect is measured, never estimated from the picture: the element's bounding rectangle from the browser for a codebase or live page, or the node's bounds for a design file, each divided by the pane's size. Where nothing can measure it, as on an image-only source, the entry carries no rect for that pane. The badge and the row's lead-in still say which part, and the row's crops are cut from the region you can name. A loose crop reads fine; a guessed outline reads broken.
    
    ## Capture
    
    Capture it with whatever the host drives a browser with. Aim for three things: the fonts and both panes loaded before the shot, the whole board in frame, and twice the pixel scale, which makes it readable at about 2000 px wide.
    
    - **A browser you can script** gives all three: wait for `document.fonts.ready` and for both images to complete, then shoot the `.board` element at a device pixel ratio of 2.
    - **A tool that only captures a page as it stands** gets the same detail from `--board-zoom: 2` at the top of the file, which renders every pixel twice over.
    
    Either way the type stays its on-screen size, since growing it to reach the width makes a wall of heavy text. Never rasterise through an SVG to PNG converter: it fetches neither the fonts nor the panes.
    
    Then look at the capture. A board much under 2000 px wide, a blank pane, or a line you cannot read at full size means the capture missed. The board file then goes to the user in its place, with one line in the report saying the image could not be captured.
    
    ## Read it back
    
    Read the composed image before writing a word about it.
    
    1. **The marks.** Every outline encloses its whole part and cuts no text, every badge sits on a corner and covers nothing, and the marks read as notes on the render, not part of it. On a miss, correct the rect and capture again, once.
    2. **Row by row.** Where a row's BEFORE and AFTER crops read the same at that scale, the move did not visibly answer its problem. Either shrink its report entry to what the crops show, or cut the move: the row goes, the problem's entry says it stayed and why, and the problem goes back in the Deferred record.
    3. **The top block.** Credit a visible difference there to the move that made it. A landing the eye finds because of one move is not written up under another.
    
    ## Embed the images
    
    The board names its panes by path, so it only shows them while they sit beside it. A host that sends files one at a time, or a user who forwards the board, leaves them behind, and every pane and crop goes blank. Once the marks pass and the last capture is taken, put the images inside the file:
    
    ```
    python3 <this skill's folder>/scripts/embed-images.py board.html
    ```
    
    It swaps each image path in the JSON for the image itself and prints the count and the new size. A path that names no file stops it with the path named and the board unchanged: fix the path and run it again. Where Python cannot run, make the same change with what the host has: each path becomes `data:<the image's type>;base64,` followed by the file's bytes in base64, such as `data:image/png;base64,iVBOR…`.
    
    Embed last. The board then runs to hundreds of kilobytes, too large to read back or edit, so a rect that still needs fixing is fixed first. Never deliver a board before this step: on its own it shows nothing.
    
    ## Deliver
    
    Deliver by what the host can show, handing the files over as [report.md](report.md) says.
    
    | Host | Deliverable |
    | --- | --- |
    | Shows images in the reply | the PNG, and the board file beside it past three moves, since the chat scales a 2000 px image down past reading size |
    | Shows no image inline | the board file, with the PNG attached |
    | Cannot capture | the board file alone |
    
    When the source is an image and not even the board file can be produced, the report's Degraded line names it. For any other source, a render that cannot be captured is described in the report and is not a degraded run.
    
  • skills/redesign-screen/references/collections.md9.2 KB3ce8a749ac25upstream ↗
    # Redesigning from a collection
    
    When the user points at what they saved on Mobbin, those screens are the evidence they chose. Step 3 reads them with `list_collections` and `get_collection_contents`, under those tools' own rules, in place of a search. Explore's step 3 uses this file the same way.
    
    ## What the user pointed at
    
    Everything the user pointed at is **the collection** in this file, however many collections it spans:
    
    - **One collection**, by name or link.
    - **Several collections** ("my checkout and paywall collections"). Read each and combine them. A screen saved in more than one counts once.
    - **Saves described in words**: by app ("the screens I saved from Duolingo"), by pattern ("my saved login screens"), or both. Read `all-saved` and keep the items whose `app_name`, `screen_patterns`, `ui_elements` or flow `actions` match.
    - **A teammate's saves** ("collections saved by <name>"). The tools don't record who saved what, so take the collections whose name includes the person ("<name>'s login", "settings (<name>)"), and say in one line which ones you took. If no name matches, say so and ask which collections to use.
    - **Any mix of these**, combined the same way.
    
    If nothing matches, say in one line what you looked for and ask whether to search Mobbin instead. Build nothing before the answer.
    
    Name the collection to the user in their words: "your saved Duolingo screens", "your "Checkout" and "Paywalls" collections".
    
    ## When a search may run
    
    - **Collection only** ("use my checkout collection", "from my saved screens"). Run no search unless the user picks one at the gap question.
    - **Collection first, then search** ("use my collection and anything else on Mobbin", "add more if you need"). Read the collection, then search only for what it lacks, without asking.
    
    "Go ahead without asking" skips the scoping round only. It doesn't allow a search.
    
    If the collection tools aren't connected, say so in one line and ask whether to search Mobbin instead. Build nothing before the answer.
    
    ## What to read
    
    - **Content type.** Screens for a screen or component, sections for a web section. A target that spans steps (an onboarding, a checkout across pages) reads flows too. If the collection holds only flows for a screen target, read them and count each flow screen as a screen.
    - **Platform.** Read the target's platform first, then the same content type on the other platform if the collection holds it. Cite those screens for structure only, like a screen from another domain, and say so once in the report.
    - **Size.** Follow the tool's limits on how much to read, and ask before reading further when the tool says to. Say in one line how many items there are and what you're looking for.
    
    ## Notes on saved screens
    
    A comment on a saved item, a **note** here, counts like a reference the user brought in the prompt. Read every note and match it to the part of its screen it names. A note that names nothing visible ("good one", "check later") changes nothing.
    
    A note decides:
    
    - **The take.** The move carries the part the note points at, not another part you noticed.
    - **The anchors.** On the taken side, a screen with a note is an anchor before one without.
    - **A tie between sides.** When two sides fix the problems about equally, take the side with the noted screens.
    
    A note doesn't decide:
    
    - **The problems.** A liked part that fixes no ticked problem is not a move. It goes to the Deferred record with its note, and the closing menu offers it as a next pass.
    - **The look.** A note that likes a reference's accent, typeface or illustration doesn't bring it into the product's design system. The Preserve list holds.
    - **The side, against the problems.** When noted screens sit on the side that fixes fewer problems, the problems win, and the report names the note not followed.
    - **Which sides explore shows.** Sides with noted screens come first, and every other side with precedent is still shown.
    
    The problems settle notes that point at different sides, like any other split. Screens without notes count as usual.
    
    ## Is it enough?
    
    Sort the collection's screens into sides and take a stand as step 3 does, from the collection alone. Then check it against what the run has to build:
    
    - **Every part of every problem.** A problem is covered only when collection screens show a fix for each part of it, as the user put it. "No period or trend" needs both a period and a trend shown. Never split, merge or narrow a problem to fit the collection.
    - **Products, not captures.** Count products as step 3 does, and only those on the target's platform. Screens from the other platform can still be cited for structure.
    
    The bar a collection must pass:
    
    - **Targeted:** every ticked problem has a move citing at least one collection screen, and the taken side has two or more products.
    - **Explore:** at least two sides, each shown by two or more products, not counting the side the baseline already follows.
    
    If it passes, build from the collection alone: no search and no gap question.
    
    ## The gap question
    
    If it falls short and the user already allowed a search, skip the question and search for what's missing, as [When a search joins](#when-a-search-joins) says. If it falls short because the user asked to build from one app in their saves ("like the Locket screen I saved", "only my Locket saves"), skip the question and stay within the collection, as option 2 below sets out, since they already chose that app. Otherwise ask one question before planning any move, with the question tool as [scoping-round.md](scoping-round.md) sets out. Ask it even when the prompt said not to ask questions, because searching beyond what the user named is their decision.
    
    Keep it short; the user knows what they saved. Two short sentences and the ask, at most 25 words: what the collection has, counted by product, and what it lacks, in the problem's terms. Don't describe the screens or explain the bar. "Your "Paywalls" collection has one iOS paywall (Calm). None shows a trial timeline. How should I fill the gap?"
    
    Single-select, in this order, each description one short sentence:
    
    1. **Search Mobbin for the gaps** (recommended). "Only for what's missing. Your screens still lead." Search as [When a search joins](#when-a-search-joins) says.
    2. **Stay within the collection.** "Build from what it shows, even one app." For this run the bar drops to one product: build any move, or in explore any side, that one collection product shows, as an example rather than precedent. In explore, each side becomes a variation, and a one-app variation follows that app across the screen as [variations.md](variations.md) sets out. A problem no collection screen shows keeps its report entry as one that stayed. Leave this option out when it would build nothing: no collection screen shows anything the target needs, or in explore the only side it shows is the baseline's.
    3. **I'll add to the collection.** "I'll wait, then read it again." Wait, and read the collection again when the user says it's ready. For saves described in words, the label is **I'll save more**.
    
    Without a question tool, put the same three in a text round that asks for a word, and build nothing before the answer. If the call fails or returns no answer, stay within the collection, since that goes no further than the user asked.
    
    ## When a search joins
    
    - **Aim it at the gap.** Search under step 3 and the `search` skill: one query per problem the collection doesn't cover, or one for a missing side. If every problem is covered but the taken side has one product, query that side's decision to find a second.
    - **Collection screens come first.** A collection screen is an anchor wherever it shows the decision. A searched screen is an anchor only where no collection screen shows it, and the side is still taken whole.
    - **Note each screen's origin**, collection or search, for the report.
    
    ## In the report
    
    - **A change or variation built from a note** says so in the user's terms: "The total now sits above Pay, as your note pointed out." The sentence doesn't name the app; it stays on the References line as usual.
    - **A note not followed** gets one clause in the plain line after the entries: the note, quoted, and why the problems went the other way. So does a note that likes a look the Preserve list keeps out: "Your note "love this green" likes a look; the redesign keeps your palette."
    - **Where the references came from.** The plain line after the entries says so, as [report.md](report.md) sets out. When every reference is from the collection: "Every change draws on your "Checkout" collection." When a search joined, it names the changes that cite screens from outside it: "Changes 2 and 3 also draw on screens from outside your collection."
    - **What the collection left out.** When the run stayed within a collection that fell short, the Degraded line names what it didn't cover and each change or variation built from one app. The closing menu offers a Mobbin search for each problem it didn't cover.
    - **Search terms.** After a run that read only the collection, the closing menu's search terms are the queries that would cover what it lacked.
    - **The boards.** The variations board and the reference board show the collections as chips on the title line, as [variations.md](variations.md) and [reference-board.md](reference-board.md) set out.
    
  • skills/redesign-screen/references/reference-board.md2.8 KB62088aceb164upstream ↗
    # The reference board
    
    The board shows the evidence behind the render: every screen a move cited, grouped by the problem it answered. It is built only on request, from the closing menu or when the user asks for the references as an image or a frame. [report.md](report.md) keeps each cited screen's `mobbin_url` and `image_url`, and says why a later board uses the files downloaded then.
    
    ## The composition
    
    One composition, handed over as [report.md](report.md) hands over files. When the run read a collection, the reply that hands it over repeats the report's line on where the references came from. Title it for the target ("Mobbin references used in the paywall redesign"), with one line under the title saying how it is grouped.
    
    - **Grouped by problem**, in the problem list's order. Each group is headed by its entry's bold lead-in from the report and the problem it answered, in the scoping option's words. A screen appears once, under the first problem whose move cites it. If it also backs other problems, a short line says so ("also problems 2 and 4").
    - **After an explore run, grouped by variation**, in letter order. Each group is headed by its entry's name and what-it-does line, with its letter as the group's `label`. The axis line is the subtitle.
    - **Each screen** is the file downloaded from its `image_url`, at a readable size, never the inline search preview: that is a low-res copy for your own reading, and on the board it blurs the evidence. Under it goes the app name as a link to its `mobbin_url`, and one line naming the decision the move adopted from it (the take), in what-is-seen terms: "timeline, then one plan card carrying both prices". No mood words, no ranking, nothing the image does not show.
    - **Collections**, only when the run read the user's collections, even if a search joined them: each one's name and `mobbin_url`, shown as chips on the title line. Saves described in words are one chip, named as the user put it ("Saved Duolingo screens") and linking to All saved. A run built from a search alone leaves it out.
    - **Only the roll call.** Every screen on the board is on a References line under Changes, or under Variations after an explore run. Pool screens no move cited, dropped screens and unused screens stay off. The count in the title is the count on the board.
    - **Form.** When the source is a design file and the connected tool can write, the board is a frame beside the render, built with that tool to the template's layout. Otherwise it is the skill's `assets/reference-board.html`: copy it next to the downloaded images, fill in its JSON and change nothing else. Capture it, then embed its images, as [before-after-board.md](before-after-board.md) does for its board. The user's theme and type are never used: the board is a Mobbin document, not a screen in their product.
    
  • skills/redesign-screen/references/report.md7.6 KB6b7d74ca3668upstream ↗
    # The report
    
    Step 8 writes the report. This file sets its shape, what each part holds, how files and links are handed over, and the closing menu. Explore mode writes its own entries under [variations.md](variations.md) and takes everything else from here.
    
    ## Before writing
    
    Reread Ground rules: measurements stay in your notes, and the report says what changed on the screen. A platform fix is reported only when it has a row in the move table. Then invoke Mobbin's `plain-writing` skill and write under its rules. It keeps the structure set here: the section headings, and each entry's short bold lead-in, which is the only bold in the entries.
    
    ## Length and order
    
    The report is the text of the reply, under the image, never a file. The image already shows each change, so the text names it and points to the evidence. The cap is 120 words for up to three moves, plus 25 for each move beyond. Count the words. Over the cap, shorten the explainers first.
    
    In order: the file links, **Changes**, **Unchanged**, the Degraded line when there is one, then the closing menu. No Direction line, no Scope line, no reasons for what stayed. Write it to this shape exactly, every run, so two reports read alike:
    
    ```
    See changes: [HTML](/absolute/path/board.html) · [PNG](/absolute/path/after.png)
    
    **Changes**
    
    1. **The lead-in.** The one sentence.
       References: [App](mobbin_url) · [App](mobbin_url)
    2. **The lead-in.** The one sentence.
       References: [App](mobbin_url)
    
    The optional plain line.
    
    **Unchanged**
    
    Part, part and part.
    
    What next?
    
    - **What it gives them:** the detail from this run.
    - **What it gives them:** the detail from this run.
    ```
    
    Headings are bold lines, never markdown headings or a heading with a colon and text after it.
    
    ## Degraded
    
    A run that lost a leg says so. After **Unchanged**, the report adds one plain line, led by **Degraded.**, naming the leg, when:
    
    - `plain-writing` could not be invoked;
    - an image could not be opened for a screen you cite;
    - the search returned nothing usable;
    - the run stayed within a collection that fell short, as [collections.md](collections.md) sets out;
    - the taken side, or a variation's side, holds fewer than two products;
    - the before and after image could not be produced, on an image source, as [before-after-board.md](before-after-board.md) sets out.
    
    A degraded run that does not say so has misreported.
    
    ## Changes
    
    One numbered entry per ticked problem, in problem-list order, numbered as the image numbers them. Each has three parts:
    
    1. **A bold lead-in** naming the new behaviour in plain interface terms (**One button finishes setup.**, **The logos state their role.**), never the problem, so the lead-ins alone read as the list.
    2. **One sentence** on what changed on the part and what the person on the screen now gets from it: "Added "Works with" above the logo row, so the logos read as tools it connects to." Claim no more than a reader can see in the move's crop row. Mark new copy as new.
    3. **A References line** under it: `References:` then the apps on the move's roll call, each a link, separated by ` · `. Before writing it, check that each app's screen shows the decision the sentence names, not only the part it changed, and take off any that does not. When none is left, the move had no roll call: cut it from the render, as step 4 would have, capture the board again, and report it under its problem as one that stayed.
    
    A problem whose move was cut keeps its entry: led by the problem in the scoping option's words, one sentence saying it stayed and why, and no References line.
    
    After the entries, at most one plain line for what the References lines do not cover:
    
    - parts of a move that no cited screen shows ("No reference shows the exact wording or placement.");
    - when every product behind the moves is from another domain, that they lend structure only;
    - a Preserve breach, or a removal the reader would not expect;
    - when the run read a collection, where the references came from: that every change draws on it ("Every change draws on your "Checkout" collection."), or, when a search joined it, the changes whose References include screens from outside it;
    - a note on a saved screen that the render did not follow, and why, as [collections.md](collections.md) sets out.
    
    Nothing to say, no line.
    
    Keep the direction, the side not taken and why in your notes. A later why is answered from them.
    
    ## Unchanged
    
    One line listing the parts of the target the render left as they were, in the user's terms, with no reasons and no citations: "Layout, styling, headline, button, plan note, logos and screenshots."
    
    ## Links and files
    
    Every app named is a markdown link to that screen's `mobbin_url`, with the app name alone as the link text. No bare URL and no footnote marker, so a reader can check a claim without asking.
    
    File links follow `See changes:` and are markdown links labelled by format, `HTML` for the board file first and `PNG` second, with the file's absolute path as the target, never a bare path. A file not produced drops its link, and the line goes when none was.
    
    Hand over every file the run produces with whatever the host has, in this order: a file-sending tool such as `SendUserFile`, an image or attachment in the reply, an artifact where the host renders one, and failing all three, the file's path on the first line of the reply. In targeted mode, only the before and after board and its two panes are written to disk; [variations.md](variations.md) lists explore's files.
    
    Asked afterwards for the links or the sources, list one line per screen the report cited, in the same link form. Dropped and unused screens get no line, and nothing appears that the report did not cite.
    
    Keep every cited screen's `mobbin_url` and `image_url`, with the decision it showed, for that list and for the reference board. The `image_url` is the full-size file and expires after thirty days, so a board asked for later uses files downloaded then. Only the `mobbin_url` ever appears to the user.
    
    ## The closing menu
    
    After **Unchanged** and any Degraded line, one line asking what the user wants next, then two to four bullets. Each bullet is one option in the user's terms, led by what it gives them, and filled from this run. A bullet with nothing from the run behind it is left out. The candidates, in this order when they apply:
    
    - **What this pass left out.** The Deferred record's top entries as a second pass ("a second pass on the promo code and the renewal line", "move the promo banner to checkout, as Calm does"), and the direction or scope not taken: the other side by its apps, the whole screen after a component pass, the other theme. After a run that stayed within a collection that fell short, a search of Mobbin for each problem it didn't cover. A decision the user's note on a saved screen liked, when no ticked problem needed it, as a next pass that names the note. Present whenever the Deferred record holds anything, a side was left, or a collection fell short.
    - **The reference board.** Every screen behind the render laid out as one image, with what each showed, so the user can see the evidence beside the result. Its form follows the source: a frame in the connected design tool when that tool can write one, a PNG otherwise.
    - **Search terms to keep going on Mobbin.** The queries this run made, reworded in the user's terms, plus the ones that would cover the Deferred record, so they can continue the research themselves. After a run that read only a collection, the queries that would cover what it lacked.
    - **Apply it.** After an image-only run, the same moves made in the code once they point at it, re-entering at step 2.
    
    `plain-writing` keeps the line and the bullets: they are the caller's structure, not a sign-off.
    
  • skills/redesign-screen/references/scope-and-preserve.md8.8 KBc95a8c0d7ba4upstream ↗
    # Scoping rules
    
    A redesign goes wrong before the first reference is opened. The intent was fuzzy, so more was redesigned than was asked for, or the right thing was redesigned and its neighbours were rebuilt with it. This file holds the rules the scoping answers lean on, for whatever form the source takes. How the questions are asked is in [scoping-round.md](scoping-round.md).
    
    ## Three intents
    
    | Intent | What the user points at | Mobbin tool | The failure it invites |
    |--------|-------------------------|-------------|------------------------|
    | **Screen** | a route, a file or a full image of one view | `search_screens` (platform required) | rebuilding the shell (nav, header) the screen shares with every other screen |
    | **Component** | one region inside a screen: a table, a card, a form, a sidebar | `search_screens` on the screen the component lives on, then read the region | changes leaking outside the region; a shared component rebuilt, and every screen that renders it changing with it |
    | **Section** | one block of a web page: hero, pricing, feature grid, testimonial, footer | `search_sections` (web only, no platform argument) | treating a section reference as a screen reference and inheriting the page's whole layout |
    
    Decide which one this is, say it in the Scope line, and never mix references across two of them. A section reference tells you nothing reliable about a component. A whole-screen reference pulls a component redesign outward into the chrome around it.
    
    ## Naming a region
    
    For a component, the region has to be nameable, so the render can be checked for anything outside it that moved. Name it three ways in the Scope line:
    
    1. **By the marker the source carries.** In a codebase, the file and symbol, or a wrapper, a `data-` attribute or a named slot the source already has. In an image, the visible boundary: the edge of the card, the rule above the table, the frame of the panel.
    2. **By what the user sees.** "The table and its filter row, from the tab strip down to the pagination footer."
    3. **By what is outside it.** "The page title, the search box above the tabs, and the sidebar are not part of this."
    
    A wrapper shared with other screens is outside the region.
    
    **A shared component** is rendered by other screens too. Stop and say so: redesigning it is a design-system change, because every screen that renders it inherits it. The user chooses between a local variant for this screen and a shared change every one of those screens takes on. Do not choose for them. In an image-only run, ask whether the same component appears elsewhere in the product; when the user does not know, treat it as local and say so in the Scope line.
    
    ## Platform
    
    Read the platform off the source as one of the two values the tools take:
    
    - **`web`**: a web viewport or a web framework.
    - **`ios`**: a phone frame or a status bar. An Android source searches `ios` too, the only phone platform the corpus carries, and the Scope line says so.
    
    When it is not clear, fold it into the first scoping question as a single-select. Pass it to `search_screens` and `search_flows` so references come back in the right form factor.
    
    Write the moves in the platform's words: click, hover and viewport on web; tap, thumb and sheet on a phone. Discard a decision that rests only on a form-factor fact the platform does not have (thumb reach under a pointer, a bottom sheet on desktop).
    
    ## Crops
    
    A fragment (content cut at the frame edge, or one region lifted out with no chrome around it) is a section of a screen, not a screen. Say so once. Judge only what is in the frame, and never call something missing that could sit off-frame. If whether an element exists matters to a move, ask.
    
    ## Relaxing a default
    
    Three defaults hold in every run: the design system and theme, the data and routes, and no new dependencies. A user who asks to relax one is asking a question, not granting a permission. Each request gets one follow-up before scoping continues, and the answer goes into the Scope line.
    
    - **Design system and theme.** Ask: "Do you want this to take on the reference's look?" A yes is a theme change, a separate job with its own scope, and the Scope line says so; the redesign itself still ships inside the user's design system. A no, or a shrug, keeps the default, and you say nothing more about it.
    - **Data and routes.** Ask which values or destinations may change, and why. A move that needs different data (a new derived value, a different sort) goes into the Scope line as the exception. A change to the data itself, or to where a control goes, is a separate job, and the Scope line says so.
    - **No new dependencies.** Ask which additions are allowed (a library, a typeface, an icon set). Record them in the Scope line as the exceptions; everything unnamed stays out.
    
    **Copy left unticked** is not a relaxed default; it means what it says. Text may change where a move needs it, and every change is named: a new element gets a label, and an element a move restructures may get a shorter or clearer one. Each added or changed string is listed in the Preserve list as changed text and marked as new in the report. Ticked or not:
    
    - the domain never changes: no string swaps the product's entity or job for the reference's ("New customer" becoming "Create post", the reference's tagline, its plan names, its example rows). That is a copy job, scoped separately;
    - numbers, names and routes stay as they are.
    
    ## Look words
    
    A look word ("more vibrant", "less cluttered", "more professional", "less slop") names no part and no fault, but it points at one kind of decision on the screen. Before the mode is picked, translate the word into what it reads as on this screen, and let that decide the mode and, in explore, the axis. The user's word stays theirs in every line they read; the report says what moved.
    
    | The user says | It reads on the screen as | Mode | Axis or problems |
    |---------------|---------------------------|------|------------------|
    | more vibrant, bolder, more colour, more playful | where the product's existing colour roles sit | explore | colour placement: which surfaces and parts carry the accent and the system's other colour roles (a filled header, a tinted plan surface, coloured icons) |
    | less cluttered, cleaner, simpler, more minimal | how many things compete, and how they are grouped | explore | density: what the side merges, groups or leaves out, with all content kept as step 4 requires |
    | more professional, more polished, more premium, more serious | how consistently and sparingly the screen is treated | explore | restraint: how much colour, how many type sizes and treatments, how much ornament the side's screens carry for the same job |
    | less slop, less generic, less AI-looking | the habits [taste.md](taste.md) names, found on the baseline: gradients, pill badges, an icon beside every label, a subtitle restating its title, a card around everything, emoji, filler copy | targeted | each habit found is a candidate, named by the part that carries it, and the Problems question lists them; each fix is a move whose roll call shows the same part without the habit |
    
    - **Colour placement spends only the colour roles the system already has**: no new hue, no gradient it does not carry. When it has none to spend (a monochrome system, one accent already on everything), the Preserve follow-up runs before scoping continues, since a new palette is a theme change and a separate job.
    - **Slop is targeted** because its fixes are a list, not a direction: three variations of removing the same gradient differ in nothing.
    - **A word the table does not carry**, or one the baseline shows nothing for ("more professional" on a screen that is already sparse), gets one question before the round: what on the screen reads that way, single-select, the options drawn from what the source shows. The answer maps to the nearest row.
    
    ## The user's register
    
    How the request is worded predicts how the redesign will go wrong, and which guardrail to lean on:
    
    - **"Redesign my X."** No reference, no constraint. Left alone, the render gains elements nobody asked for. Ask the full round, lean on the Preserve list, and cap the moves.
    - **"Using this as reference, redesign my X."** A reference, no style constraint. The reference's brand bleeds in here: the background flipped from light to dark, the accent changed, its typeface and wordmark appearing. Lean on step 5's rule to take the reference's structure, not its look.
    - **"The Y in my X is the problem, help me with that."** The user brings the diagnosis. Take it word for word as the first problem, aim the search at it, and answer it first. Add what you see beside it, marked as yours. Never rank your reading above theirs, or widen the job past what they pointed at without saying so.
    - **"Rebuild my X to match the structure and layout of this, keeping our own styling."** The explicit form, and the one every run is held to, whatever the user's wording.
    
  • skills/redesign-screen/references/scoping-round.md8.6 KB7e7a69ec8d31upstream ↗
    # Asking the scoping round
    
    Step 1 of the skill writes the job and walks the path. This file sets how the questions that follow are asked, what each one offers, and what the Scope line records. The rules the answers lean on (intents, regions, platform, relaxing a default, look words) are in [scope-and-preserve.md](scope-and-preserve.md). The options are the first text the user reads, so reread Ground rules before writing any.
    
    ## How a round is asked
    
    Decide from the tools you can call in this turn, never from the host's name or from habit:
    
    - **A question tool is callable** (`AskUserQuestion` in Claude Code, `request_user_input` in Codex where the mode exposes it, or the like): ask with it, one call per round, fitted to its schema. Where it takes fewer options than a question needs, drop the lowest-ranked candidates first, never Help me decide.
    - **None is callable**, including a host that has one in other modes: fold every round into one **proposed scope**, below. Do not open a round you cannot ask and then describe it.
    - **The call fails or returns no answer**: use the proposed scope for the rest of the run. Do not call the tool again.
    
    Only a reply that called the tool may mention a panel, a picker, options to select, or anything "below" to click. A text round asks for a word in reply, and says so.
    
    Ask only what the prompt leaves open. A prompt that names the target, the fault and the constraints skips the round, and the Scope line records what it took from the prompt. "Redesign my settings page" gets the full round.
    
    One line comes before the questions, saying what the source is (a screen or a crop, the platform, the kind of product) so the options can be concrete: "An iOS paywall screen." Never describe the screen back to the user or give its dimensions; they have the image.
    
    ## The order of the rounds
    
    1. **Mode**, unless a flag set it. Always the first question the user sees, alone in its round, because it decides which questions follow.
    2. **Source**, when the codebase question applies. A round of its own.
    3. **Target, Problems and Preserve**, in one round. Platform folds into Target when it is not obvious. Explore mode drops Problems, as [variations.md](variations.md) says.
    
    ## Mode
    
    Single-select. The option the prompt points to comes first, marked recommended:
    
    - **Explore** when the prompt names no fault, part or reference ("make this better").
    - **Targeted** when it does ("the trial button is buried", "like Calm's").
    
    A look word ("more vibrant", "less cluttered", "less slop") is not open-ended. It maps to a mode and an axis as [scope-and-preserve.md](scope-and-preserve.md) sets out, and the recommended option follows that.
    
    ## The codebase question
    
    It applies when the only input is an image and nothing in the working directory renders it. Headed Source, single-select, one line on what each answer gets:
    
    - **Point me at the code.** The region is named by file and symbol, the state list comes from what the source can render, the render is edits to the source, and the result can be checked.
    - **Image only.** The region is named by what is visible, every state but the pictured one is missing until the user says otherwise, and the render is the before and after image alone.
    
    It comes before the scoping questions because they are written from whichever source the answer names. A codebase that arrives after the moves are written sends the run back to step 2.
    
    ## The three scoping questions
    
    **1. What exactly is the target?** Single-select: the three intents, each with the concrete thing you saw. The whole screen, one component in it (name the region; everything outside it is frozen), or one web section (hero, pricing, feature grid, footer). A crop is a section, not a screen, and the question says so.
    
    **2. Which of these should this pass fix?** Multi-select from the ranked candidates of the job walk.
    
    - **The user's own complaint is not an option.** It is already in the problem list, and the question's first line says so: "Your point about the trial button is in. Which of these should this pass also fix?"
    - **A metric or a named reference is never an option.** It goes into the Scope line in the user's words, and the candidates that block it are what gets ticked.
    - **Label and description.** The label names the break in the target's own terms. The description names the step of the job it blocks, then what on the screen shows it. Label: "Plan and trial button do not lead". Description: "choosing a plan is the first step here, and the yearly plan and the trial button sit below the comparison table, past the first view on the phone, so the choice is made after a scroll". The pixel measurement behind it stays in your notes.
    - **Four options.** The first is always **Help me decide** ("I'll pick the ones that block the most of the job"). The three highest-ranked candidates fill the rest. Every other candidate goes to the Deferred record.
    - **What the ticks mean.** The ticks are the **problem list** for this pass, in the order ticked, then by rank. Help me decide, no tick, or "just go" takes the top three by rank. Help me decide ticked beside candidates takes those and tops them up to three by rank.
    - **After the round**, one line names what was picked, one short clause each with the step it blocks. The run carries on without asking again.
    
    **3. What else must survive?** The question tool cannot pre-tick, so what holds in every run is never an option. The question's first line states it in this product's terms: "The design system, the data and routes, and the installed dependencies hold. What else must survive?"
    
    - **Four options.** The first is always **Help me decide** ("I'll change only what makes the experience better"), so the user has an answer to pick instead of leaving the question blank. The rest come from what the source showed: the copy word for word, the layout footprint, and a named element a redesign might otherwise touch (the hero illustration, the comparison table).
    - **What the ticks mean.** Help me decide, or no tick, means only what holds in every run holds. Everything else may change where a move needs it, and nowhere else. Help me decide ticked beside other options keeps those too.
    - A user who wants a default relaxed types it in the free-text option. That triggers one follow-up before scoping continues, as [scope-and-preserve.md](scope-and-preserve.md) sets out.
    
    ## The proposed scope
    
    Without a question tool, the rounds fold into one reply. It states the Scope line the run would proceed on and asks for one word. It is still a round: nothing is built before the answer.
    
    Short lines, each with a bold lead-in, in this order:
    
    - **Mode.** When no flag set it: the recommended mode and the word that switches it. "Explore: three directions to pick from. Say targeted to fix named problems instead." The lines under it are written for that mode.
    - **Source.** When the codebase question applies, in place of its own round: "Image only. Point me at the code if there is some, and the changes land there."
    - **Target.** What the source read as.
    - **Fix in this pass.** The top three candidates, each with what on the screen shows it.
    - **Also seen, not in this pass.** Every other candidate, so nothing deferred is hidden.
    - **Holds.** What holds in every run, and anything the prompt asked to keep.
    
    Then one closing sentence: say go, or name the change, with the edit paths in the user's terms (a narrower target such as the plan cards only, a problem to add or drop, something else that must stay). "Go" and any edit map onto the Scope line the same way ticks do. "Show me the others" gets the lettered lists in reply. The run stops until the user answers.
    
    ## The domain
    
    The **domain** is the kind of product, in a word or two: finance, health, developer tools, food delivery. Read it off the source (the content, the units, the vocabulary on the screen), and ask only when that fails. The Preserve list's first item holds the render to it, and the report weighs a cross-domain roll call against it. A run that cannot read it says so rather than guessing.
    
    Platform is read the same way; [scope-and-preserve.md](scope-and-preserve.md) sets how.
    
    ## The Scope line
    
    Assume only once you have asked. A skipped question or "just go" proceeds on labelled assumptions, and an ambiguity that survives asking takes the narrowest reading.
    
    Every answer and assumption goes into one **Scope line**: intent, region, platform, domain, source form, the job, the problem list in the user's words, the constraints. It is fixed after step 1 and kept in your notes; the report never prints it. Anything it records that the user has not been told (an Android source searched as iOS, a mockup in place of a frame) is said in the line before the scoping questions, or in the proposed scope.
    
  • skills/redesign-screen/references/taste.md4.1 KB4a03983bf954upstream ↗
    # Taste: how a judgement call is made
    
    A reference decides what a move does, never how it lands in this product. The decision is rebuilt in the product's own components, spacing and words, and some parts no screen shows at all: where a new label sits, what it says, where a displaced control lands. Those calls are your judgement. Left alone, judgement drifts to the same habits in every product: a gradient, a pill badge, an icon beside every label, a subtitle restating the title, a card around everything. This file replaces those habits with rules that read the product in front of you. Steps 4 to 6 of the skill use it.
    
    ## Where it applies
    
    Every move, in how it is built. Authority runs in this order:
    
    1. The Preserve list and the design system.
    2. This file, for how every part is built: its size, rank, spacing, alignment and wording.
    3. The cited screens, for what each move does: the decision it makes and where its parts sit.
    
    A cited screen informs a move but never decides how a part is built. When a screen and this file disagree, this file wins: a screen that makes a part big doesn't make it big here if this product's job ranks it lower. This file never starts a move; step 4 decides what gets a move. A part a cited screen shows is covered by its References line. A part no cited screen shows is your judgement, and the report grades it so, as Ground rules set out. These rules are how the call was made, not evidence, and are never named to the user.
    
    ## Dealbreakers
    
    A render that breaks one is fixed before capture, whatever a reference shows.
    
    1. **Don't add for the sake of adding.** Every element the render gains answers a move's problem. Empty space is not a problem: leave it, and add no icon, badge, divider, shadow, background panel or illustration a move does not need. Step 4's cut for stock details still applies.
    2. **Don't add text the job does not need.** No subtitle restating a heading, no helper line under a clear control, no label on something the screen already explains. When a move needs words, write the shortest plain label, in the case, tense and tone of the copy around it, claiming nothing the source or the user's words do not show.
    3. **Don't add a gradient the design system does not have.** Use one only where a token or an existing component already does. Otherwise use the flat colour role of the nearest sibling.
    4. **Don't blend sides.** A render, or a variation, carries only its own side's decisions, even where another side's screen looks better for one part. That part stays as the baseline has it, or as the taken side's screens show it.
    
    ## The rules
    
    None says how anything looks; the look is the product's. They decide only relations.
    
    1. **Change the least that answers the problem.** Rearrange before you restyle. A part the move does not need keeps its position, size and treatment. This limits what a move touches, never how fully the taken side is adopted.
    2. **A new element copies its nearest sibling**: the component, size, weight, colour role and spacing of the closest element doing the same kind of job. Nothing gets a treatment the screen does not already use.
    3. **One thing leads.** A new or changed part sits at or below the rank of what it serves and never outranks the first step of the job, whatever rank it has on a cited screen. Giving one thing more weight gives nothing else more.
    4. **Things sit beside what they are about.** A label goes on the side of its subject the product already uses, usually above or leading. The gap inside a group is smaller than the gap between groups.
    5. **Use the lines already there.** A new element aligns to an existing edge and sits on an existing spacing step, adding no new alignment line.
    6. **The same thing looks the same.** Two elements doing one job in the target get one treatment.
    7. **The platform's positions hold.** Primary action, back, close and dismiss go where the platform puts them, as the product already does.
    
    ## Reading the result
    
    At step 6, check the dealbreakers first, then compare each added or changed part with its sibling. If a reader could tell which part was added by how it looks, rule 2 was broken. If the part draws the eye before the thing it serves, rule 3 was.
    
  • skills/redesign-screen/references/variations.md16 KB3af1f99407eeupstream ↗
    # Explore: up to three variations, one side each
    
    An open-ended request ("make this better") names no fault, so one design has to guess the direction as well as build it, and the guess is usually the additive one. Explore mode hands the choice to the user: up to three variations, each built whole from one side of the Mobbin references, shown as mockups beside the baseline. Nothing is applied until one is picked.
    
    The skill's Mode section sends a run here. The sections below replace the skill's steps 2 to 8 where they say so. Everything they do not mention holds as the skill states it.
    
    ## What holds
    
    - Ground rules, Inputs, and writing under `plain-writing`.
    - The Preserve list and how Apply holds it.
    - [taste.md](taste.md), and step 4's cuts for stock details and dropped content.
    - **Every variation is backed by precedent:** two or more products on its side, each a screen you looked at. A side only one product shows is an example, not a direction, so it is not shown. The exception is when the user chose to stay within a collection that fell short (section 3).
    
    ## 1. Scope
    
    After Mode, ask Target and Preserve as [scoping-round.md](scoping-round.md) words them. There is no Problems question: the user named no fault, and picking three from a list is the decision this mode exists to avoid.
    
    - **The line before the questions** says what the run returns: "An iOS paywall screen. I'll show up to three directions to pick from."
    - **A proposed scope** carries **Mode.**, **Source.** when the codebase question applies, **Target.** and **Holds.**, and no fix list.
    
    Still write the job and walk the path, in your notes. The candidates are not a work list here. They decide whether a component beyond the axis may change (section 4), and section 5 checks each variation against the same six checks. They go to the Deferred record as usual.
    
    ## 2. Freeze the baseline
    
    As the skill's step 2, without the problem list. Read the design system from the source, since every variation is a mockup in it: the tokens or visible palette, the type and spacing scale, and the components. In a design file, that is the variables and components the design tool's read calls return.
    
    The **BEFORE pane** is the user's input as given: their screenshot, or a capture of the running screen, with its own time and status bar, blurry if it came blurry. Nothing is redrawn on it. When the input is several screenshots of one scrolling screen, `before` lists them in scroll order, each labelled after the first ("Scrolled"), and the board shows each as its own card. Never join them into one image: the tab bar and headers that stay on screen would land mid-page, and the parts they share would show twice. The baseline mockup still rebuilds the screen as one full-height page.
    
    Then build the **shell** every variation starts from, so the variations differ only where their sides do. Copy the skill's `assets/shell.html` and `assets/chrome/` next to the captures and fill it in:
    
    - **Device.** The input's width in points (CSS pixels on the web) and the platform. There is no height: the baseline mockup and each variation are as tall as their own content, whatever height the input or its frame has.
    - **System chrome.** On iOS, the status bar, home indicator and keyboard come from `chrome/`, drawn at 9:41 with full signal, wifi and battery, whatever the input showed. Set the keyboard only when the input shows one, light or dark as it is. Set the glyph colour from the input's top background. Never crop system chrome from the input into a mockup.
    - **Frame.** Everything outside the region, rebuilt from the input in the design system: navigation, tab bar, the parts of the screen the target leaves alone.
    - **Region.** The target, rebuilt as the input has it, between the region markers.
    
    The filled shell is the **baseline mockup**. It is a working file, not shown on the board. Capture it and compare it with the input outside the system chrome. Fix in the shell any difference in layout, type or colour a reader would see at the board's scale, before any variation starts, since every variation inherits it.
    
    ## 3. Search wide and find the axis
    
    Aim the search at the target's job, not at faults, so the results are broad enough to disagree:
    
    - two or three queries: the target as a state, the target by the decision it turns on, and the journey it sits in;
    - then one more pull of unseen results: one of those queries sent again, as Mobbin's `search` skill sets out.
    
    Every query here runs `deep`, since each side has to show a decision. Everything else in the `search` skill holds. Look at every image and count products, not captures.
    
    **From a collection.** When the user points at their saves, read them as [collections.md](collections.md) sets out, in place of the queries above. The axis and sides below are found within it, and a search joins only where that file allows, with the queries aimed at the missing side.
    
    **Find one axis.** A screen makes many structural decisions (how plans are chosen, where the action sits, what leads the top). Sides per component would multiply into combinations no one can compare, or blend into a remix. So a run explores one axis: the decision the results split on most that also shapes most of the target.
    
    - On a paywall it is usually how the plan is chosen: a comparison table, stacked plan rows, or one card with a toggle. For a component target, it is that component's main decision.
    - When the screens differ most in how they're arranged (what comes first, how big each part is, where the action sits), make the arrangement the axis, even if their controls differ too. Each variation copies its side's whole arrangement, including where the action sits: if its products keep the action visible without scrolling, so does the variation.
    - When the request was a look word, the axis is the one [scope-and-preserve.md](scope-and-preserve.md) maps it to (colour placement, density or restraint). Sides are still what two or more products visibly do, never a mood.
    - **The axis line** names it for the report as the choice the user makes: "Pick how the plan is chosen." It never narrates what the screens or the variations did ("They differ in…", "The screens split…").
    
    **Record the sides.** A side is a control pattern and hierarchy on the axis, visible in two or more products. Record each like the skill's step 3: its apps, the decision they share, and per screen the `mobbin_url`, `image_url`, take and leave.
    
    - The side the baseline already follows is not a variation. Within a collection, a side whose one product makes the decisions the baseline already makes, on the axis and on the parts the screen shows, is the baseline's side too: it would build a look-alike.
    - Keep up to three sides, the most different from each other first. Fewer sides means fewer variations, never a padded one.
    - One side is shown alone, and the line under **Variations** says it was the only direction with precedent.
    - If no side has precedent, say so in one line, build nothing, and offer a targeted run in the closing menu. If the user chose to stay within a collection, follow the next bullet instead.
    - **Staying within a collection.** If the user chose to stay within a collection that fell short, also build each side that only one of its products shows, next to any side with precedent, as [collections.md](collections.md) says. Section 4 says how to build it. If the only side left is the one the baseline already follows, say so in one line, build nothing, and recommend a Mobbin search in the closing menu.
    
    ## 4. Build each variation from its side
    
    A variation is its side taken whole, as the skill's step 3 defines it. Nothing in it comes from another side. Away from the axis, change a component only to fix a break the walk found, and only the way a product on its side fixes it. One product is enough, and the entry's Also: line says the change rests on one of its apps. Otherwise keep the component as the baseline has it, because a difference between variations with no reason makes them harder to compare. A side from one collection app is the exception, as the next paragraph says.
    
    The exception is a side that one collection product shows, built because the user chose to stay within the collection. That one product is the whole side, so the variation follows it across the screen. Every part that both the target and the product's screen have follows the product's decision, built as [taste.md](taste.md) sets out and within the Preserve list. Parts that only the product has are left out. Without this, the variation would differ from the baseline only on the axis, which is too little for the user to judge that app's approach. Parts that only the target has stay, and the product's treatment is fitted around them: if the product's button is narrow because it has no fields above it, the target's button stays as wide as the target's fields.
    
    Each variation has its own move table, as the skill's step 4 writes one, with two changes:
    
    - the problem column holds the step of the job the move changes (for the axis move, the step the axis decides);
    - every roll call comes from that side's products only.
    
    **Mockups, for every source.** A variation is a copy of the baseline mockup with only its region changed, in the design system, captured at the input's scale factor.
    
    - Nothing outside the region markers changes, except `data-glyphs` when the top background turns from light to dark or back. The chrome, frame and device size stay as the shell has them.
    - The height follows the content. A variation that drops parts is shorter, one that adds parts is longer, and none is padded to match.
    - The source is not touched: no code edits and no design-file frame until a variation is applied. So all of them are built the same way and in parallel.
    - No interactions: a mockup is a picture of the state the baseline was captured in.
    
    **In parallel where the host can.** When the host runs subagents (an `Agent` or task tool), start one per variation in the same turn, each with a brief that stands alone:
    
    - the job, the Preserve list, the design system and the input;
    - the baseline mockup's path to copy, and the rule that only its region changes;
    - its side: the decision, and each screen's downloaded `image_url` file, take and leave;
    - its move table;
    - the path to [taste.md](taste.md) and the mockup rules above;
    - what to return: the mockup's path, its capture's path, the move table as built, and which parts were judgement.
    
    A subagent does not search, read the other sides, touch the chrome or frame, or write to the source. Without subagents, build them one after another.
    
    ## 5. Check each variation
    
    Read each capture as the person the job names, through the six checks of the job walk plus three for the mockup alone:
    
    - it carries only its side's decisions;
    - nothing outside the region differs from the baseline mockup;
    - no dealbreaker in [taste.md](taste.md) is broken.
    
    Two rounds. A variation still failing is dropped and named in one line of the report.
    
    Then check the variations against each other, not against BEFORE. The captures are the same width, and each file matches the baseline mockup outside its region markers, except `data-glyphs`.
    
    - **Where the host can run a command,** diff the files, not the pixels. The status bar sits over the screen, so its pixels change with a variation's background, and a pixel diff there reports false faults.
    - **Where nothing can run,** compare the status bar, home indicator and keyboard across the captures by eye at full size.
    - **A variation that differs outside its region** is rebuilt from the baseline mockup with its region alone. The chrome is never patched to match.
    
    ## 6. The variations board
    
    Copy the skill's `assets/variations.html` next to the captures, fill in its JSON and change nothing else. Capture it as [before-after-board.md](before-after-board.md) captures its board. The mockups, their captures and this board are the only files written to disk.
    
    - **Panes.** BEFORE, then one pane per variation, headed by its letter alone. Above each screen sit the entry's name, what-it-does line and References line (each app's `mobbin_url`), so a reader meets the direction before the picture.
    - **The rest of the JSON.** The title, the axis line, the theme and the meta line ("iOS paywall, 3 variations, 7 references").
    - **Collections**, only when the run read the user's collections, even if a search joined them: each one's name and `mobbin_url`, shown as chips on the title line. Saves described in words are one chip, named as the user put it ("Saved Duolingo screens") and linking to All saved. A run built from a search alone leaves it out.
    - **No marks or crops.** Unrelated redesigns share no numbered parts.
    
    Then read it back. Each variation differs visibly from BEFORE and from the others on the axis. Two that read the same at the board's scale are one side, and the weaker is dropped.
    
    Last, embed its images as [before-after-board.md](before-after-board.md) does, so the board file opens on its own.
    
    ## 7. Report
    
    Written as [report.md](report.md) writes the report, to a cap of 150 words. In order: the file links (`See variations: [HTML](path) · [PNG](path)`), **Variations**, **Unchanged**, the Degraded line when there is one, then the closing menu. Headings, file links and the closing menu follow report.md. The entries follow `plain-writing`'s shape for options, with **Variations** in place of **Changes**.
    
    **Variations** opens with the axis line, then one entry per variation. Its lines come in this order, and an optional line with nothing in it is left out:
    
    1. **A bold short name** with its letter (**A. One card, two prices**), naming the side as it looks on this screen.
    2. **What it does**, one sentence starting with the letter ("A puts both prices on one card with a yearly and monthly toggle."), checked against its pane. It describes this product, never a reference's labels or menu items. Skip it when the name already says it.
    3. **References:** the apps on the side, each a link to its `mobbin_url`, each checked to show the side's decision, as report.md checks it. Apps are named on this line only, never inside a sentence.
    4. **Downside:** the cost of this variation, as a fact its pane or its side's screens show. Optional.
    5. **Also:** one fact that matters to the choice and fits no other line. Optional.
    6. **My opinion is to** …: one sentence on this variation, when you have a view no reference backs. Optional.
    
    Only the name is bold; the labels (References:, Downside:, Also:) are plain text. About 30 words per entry.
    
    After the last entry, if the run read a collection, add one line on where the references came from, worded as in [report.md](report.md): "Every variation draws on your "Checkout" collection." or "Variation C also draws on screens from outside your collection." This line is required. Then two optional lines: an opinion on every variation, starting "My opinion is to", and "Not confirmed:" naming in a few words what the run did not confirm. No summary or comparison line after them. **Unchanged** lists what every variation kept.
    
    The closing menu, filled from this run:
    
    - **Apply one.** "Apply B" builds it in the source's form: edits for a codebase, a new frame beside the original in a design tool that can write one, a PNG for an image.
    - **Combine.** "B with A's price line" applies B with one decision from another variation.
    - **The reference board**, grouped by variation.
    - **Search terms to keep going on Mobbin.**
    
    ## Applying a variation
    
    Picking one runs targeted mode from the skill's step 4, with scope, Preserve list and references already locked. The variation's side is the taken direction, and its move table is the plan, each row's problem the step it changes.
    
    - **A combination** adds one move for the borrowed decision, with its own roll call from the other side's products. It replaces what the chosen side did in that component only.
    - **A borrowed decision that needs more than one component** is a new side, not a combination, and the reply says so.
    
    Then the skill's steps 5 to 8 run as written: applied in the source's form, checked, the before and after board, and a targeted report.
    
  • skills/redesign-screen/assets/board.html17 KBda5a63c62eecupstream ↗
    <!doctype html>
    <!--
      Before and after board for the redesign-screen skill.
    
      Fill in the JSON in #board-data and change nothing else. The layout, the type
      and the callout marks are already set here, so a run writes data, not CSS.
    
      Deliver it either way:
      - as a PNG: open this file in a browser and capture the .board element at twice
        the pixel scale, after the panes have loaded. A capture tool that cannot set
        a device pixel ratio gets the same detail from --board-zoom: 2 below.
      - as itself: run scripts/embed-images.py on it first, which puts the pane
        images inside the file, so it opens on its own and goes without them
    
      Rects are percentages of the pane image they sit on, so they hold at any
      capture scale. Crops are cut from the same two pane images, so a move needs no
      screenshot of its own.
    -->
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <title>Before and after</title>
        <style>
          /* Mobbin design system tokens, verbatim from ux-audit-tool's shared/design-system.css. */
          :root {
            --gray-0: #ffffff;
            --gray-50: #fafafa;
            --gray-100: #f5f5f5;
            --gray-150: #ededed;
            --gray-200: #e5e5e5;
            --gray-300: #d4d4d4;
            --gray-400: #a3a3a3;
            --gray-500: #737373;
            --gray-600: #525252;
            --gray-700: #404040;
            --gray-800: #262626;
            --gray-900: #171717;
            --gray-950: #0a0a0a;
            --bg: var(--gray-0);
            --bg-subtle: var(--gray-50);
            --bg-muted: var(--gray-100);
            --fg1: var(--gray-950);
            --fg2: var(--gray-600);
            --fg3: var(--gray-500);
            --fg-muted: var(--gray-400);
            --border: var(--gray-200);
            --border-strong: var(--gray-300);
            --border-subtle: var(--gray-150);
            --accent: var(--fg1);
            --accent-fg: var(--gray-0);
            --success: #1a7f46;
            --warning: #b45309;
            --danger: #b42318;
            --info: #1c5fd4;
            --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
            --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
            --fs-11: 11px;
            --fs-12: 12px;
            --fs-13: 13px;
            --fs-14: 14px;
            --fs-15: 15px;
            --ls-normal: -0.005em;
            --ls-widest: 0.12em;
            --sp-2: 8px;
            --sp-3: 12px;
            --sp-4: 16px;
            --sp-5: 20px;
            --sp-6: 24px;
            --sp-8: 32px;
            --sp-10: 40px;
            --r-sm: 5px;
            --r-md: 8px;
            --r-lg: 12px;
            --r-xl: 16px;
    
            /* The callout colour, Mobbin's blue. var(--warning) when the product's accent is blue too. */
            --call: var(--info);
            --call-fg: #fff;
            --board-width: 1100px;
            --board-zoom: 1; /* 2 doubles every pixel, for a capture tool with no scale factor of its own. */
          }
          :root[data-theme="dark"] {
            --bg: var(--gray-950);
            --bg-subtle: var(--gray-900);
            --bg-muted: var(--gray-800);
            --fg1: var(--gray-50);
            --fg2: var(--gray-400);
            --fg3: var(--gray-500);
            --fg-muted: var(--gray-600);
            --border: var(--gray-800);
            --border-strong: var(--gray-700);
            --border-subtle: var(--gray-800);
            --accent: var(--fg1);
            --accent-fg: var(--gray-950);
            --success: #46c485;
            --warning: #e0a34a;
            --danger: #f2a099;
            --info: #7aa7f0;
            --call-fg: var(--gray-950);
          }
    
          * {
            box-sizing: border-box;
          }
          html,
          body {
            margin: 0;
            background: var(--bg-subtle);
          }
          /* Centred when the file is opened; the capture shoots .board alone, so it is unaffected. */
          body {
            display: flex;
            justify-content: safe center;
            padding: var(--sp-10) var(--sp-4);
          }
          .board {
            flex: none;
          }
          .board {
            zoom: var(--board-zoom);
            width: var(--board-width);
            padding: var(--sp-10);
            background: var(--bg);
            border: 1px solid var(--border);
            border-radius: var(--r-xl);
            color: var(--fg1);
            font-family: var(--font-sans);
            font-size: var(--fs-15);
            line-height: 1.5;
            letter-spacing: var(--ls-normal);
            -webkit-font-smoothing: antialiased;
          }
    
          .docmeta {
            display: flex;
            justify-content: space-between;
            gap: var(--sp-4);
            margin-bottom: var(--sp-6);
            padding-bottom: var(--sp-5);
            border-bottom: 1px solid var(--border);
            color: var(--fg-muted);
            font-family: var(--font-mono);
            font-size: var(--fs-11);
          }
          .docmeta .stamp {
            letter-spacing: var(--ls-widest);
            text-transform: uppercase;
          }
    
          .eyebrow {
            display: block;
            margin-bottom: var(--sp-3);
            color: var(--fg2);
            font-size: var(--fs-12);
            font-weight: 500;
            letter-spacing: var(--ls-widest);
            text-transform: uppercase;
          }
    
          /* Screens fit their content, so the panes can differ in height: both start at the top,
             and the rows begin under the taller one. */
          .panes {
            display: grid;
            grid-template-columns: 1fr 1fr;
            align-items: start;
            gap: var(--sp-6);
          }
          .shot {
            padding: 6px;
            background: var(--bg-muted);
            border: 1px solid var(--border);
            border-radius: 14px;
          }
          .pane {
            position: relative;
            overflow: hidden;
            border-radius: var(--r-md);
          }
          .pane img {
            display: block;
            width: 100%;
            height: auto;
          }
    
          .callout {
            position: absolute;
            border: 1.5px solid var(--call);
            border-radius: var(--r-sm);
            /* A fixed gap outside the part's rect, so the outline never cuts through it. */
            margin: -6px;
            padding: 6px;
            box-sizing: content-box;
          }
          .disc {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            background: var(--call);
            color: var(--call-fg);
            font-family: var(--font-mono);
            font-size: var(--fs-12);
            font-weight: 700;
            line-height: 1;
          }
          .callout .disc {
            position: absolute;
            /* Fully outside the box on its top-left corner, so it covers nothing inside. */
            top: -24px;
            left: -24px;
            box-shadow: 0 1px 4px rgba(10, 10, 10, 0.3);
          }
    
          .rows {
            margin-top: var(--sp-8);
          }
          .row {
            display: grid;
            grid-template-columns: 300px 1fr 20px 1fr;
            align-items: start;
            gap: var(--sp-4);
            padding: var(--sp-5) 0;
            border-top: 1px solid var(--border-subtle);
          }
          .row-text {
            display: grid;
            grid-template-columns: 22px 1fr;
            gap: var(--sp-3);
          }
          .lead {
            margin: 0 0 4px;
            font-size: var(--fs-15);
            font-weight: 600;
            line-height: 1.3;
          }
          .line {
            margin: 0;
            color: var(--fg2);
            font-size: var(--fs-13);
          }
          .refs {
            margin: 6px 0 0;
            color: var(--fg3);
            font-size: var(--fs-13);
          }
          /* Same link as the reference board's app names. */
          .refs a {
            color: var(--info);
            font-weight: 500;
            text-decoration: none;
          }
    
          .crop {
            position: relative;
            overflow: hidden;
            background: var(--bg-muted);
            border: 1px solid var(--border);
            border-radius: var(--r-md);
          }
          .crop img {
            position: absolute;
            top: 0;
            left: 0;
            transform-origin: 0 0;
            max-width: none;
          }
          .crop-none {
            display: flex;
            align-items: center;
            justify-content: center;
            border: 1px dashed var(--border-strong);
            border-radius: var(--r-md);
            color: var(--fg-muted);
            font-family: var(--font-mono);
            font-size: var(--fs-11);
            letter-spacing: var(--ls-widest);
            text-transform: uppercase;
          }
          .chevron {
            align-self: center;
            color: var(--fg-muted);
            font-size: var(--fs-15);
            text-align: center;
          }
        </style>
      </head>
      <body>
        <div class="board" id="board">
          <div class="docmeta">
            <span class="stamp">Mobbin &middot; redesign-screen</span>
            <span id="meta"></span>
          </div>
          <div class="panes">
            <div>
              <span class="eyebrow">Before</span>
              <div class="shot">
                <div class="pane" id="pane-before"><img id="img-before" alt="Before" /></div>
              </div>
            </div>
            <div>
              <span class="eyebrow">After</span>
              <div class="shot">
                <div class="pane" id="pane-after"><img id="img-after" alt="After" /></div>
              </div>
            </div>
          </div>
          <div class="rows" id="rows"></div>
        </div>
    
        <script type="application/json" id="board-data">
          {
            "theme": "light",
            "meta": "iOS paywall, 2 changes, 3 references",
            "panes": { "before": "before.png", "after": "after.png" },
            "moves": [
              {
                "n": 1,
                "lead": "The trial button is in the first view.",
                "line": "It sat below the plans, past the first view, and now follows them directly.",
                "refs": [
                  { "app": "Headspace", "url": "https://mobbin.com/screens/example-1" },
                  { "app": "Calm", "url": "https://mobbin.com/screens/example-2" }
                ],
                "before": { "x": 6.2, "y": 75.6, "w": 87.7, "h": 6.4 },
                "after": { "x": 6.2, "y": 51, "w": 87.7, "h": 6.4 }
              },
              {
                "n": 2,
                "lead": "The charge is stated before the choice.",
                "line": "A new line under the hero names the renewal date and the yearly charge.",
                "refs": [{ "app": "Strava", "url": "https://mobbin.com/screens/example-3" }],
                "after": { "x": 6.2, "y": 30.3, "w": 87.7, "h": 2.6 }
              }
            ]
          }
        </script>
    
        <script type="module">
          const CROP_CONTEXT = 0.18; // Extra of the rect's own size on each side, to place the part.
          const CROP_MAX_HEIGHT = 240;
          const CROP_MIN_HEIGHT = 56; // Floor for the note that stands in for a side a part is not on.
    
          const data = JSON.parse(document.getElementById("board-data").textContent);
          document.documentElement.dataset.theme = data.theme === "dark" ? "dark" : "light";
          document.getElementById("meta").textContent = data.meta ?? "";
          if (data.title !== undefined) document.title = data.title;
    
          const panes = {
            before: document.getElementById("img-before"),
            after: document.getElementById("img-after"),
          };
          panes.before.src = data.panes.before;
          panes.after.src = data.panes.after;
    
          await Promise.all([sized(panes.before), sized(panes.after)]);
          markPanes();
          buildRows();
    
          /** A numbered badge and outline over each part a move touched, on the sides it is on. */
          function markPanes() {
            for (const side of ["before", "after"]) {
              const pane = document.getElementById(`pane-${side}`);
              for (const move of data.moves) {
                const rect = move[side];
                if (rect === undefined) continue;
                const callout = document.createElement("div");
                callout.className = "callout";
                callout.style.left = `${rect.x}%`;
                callout.style.top = `${rect.y}%`;
                callout.style.width = `${rect.w}%`;
                callout.style.height = `${rect.h}%`;
                callout.innerHTML = `<span class="disc">${move.n}</span>`;
                // The pane clips at its edge. A part touching an edge keeps its gap on the other sides only,
                // and its badge moves to the nearest outside corner that has room, so it never covers the part.
                const badge = callout.querySelector(".disc");
                const atTop = rect.y < 1,
                  atLeft = rect.x < 1;
                const atBottom = rect.y + rect.h > 99,
                  atRight = rect.x + rect.w > 99;
                if (atTop) {
                  callout.style.marginTop = "0";
                  callout.style.paddingTop = "0";
                }
                if (atLeft) {
                  callout.style.marginLeft = "0";
                  callout.style.paddingLeft = "0";
                }
                if (atBottom) {
                  callout.style.marginBottom = "0";
                  callout.style.paddingBottom = "0";
                }
                if (atRight) {
                  callout.style.marginRight = "0";
                  callout.style.paddingRight = "0";
                }
                if (atTop) badge.style.top = "calc(100% + 4px)";
                if (atLeft) badge.style.left = atTop || atRight ? "0" : "calc(100% + 4px)";
                pane.append(callout);
              }
            }
          }
    
          /** One row per move: the text, then the same part cropped from each pane. */
          function buildRows() {
            const rows = document.getElementById("rows");
            for (const move of data.moves) {
              const row = document.createElement("div");
              row.className = "row";
              row.innerHTML = `
                <div class="row-text">
                  <span class="disc">${move.n}</span>
                  <div><p class="lead"></p><p class="line"></p><p class="refs"></p></div>
                </div>
                <div data-crop="before"></div>
                <div class="chevron">&rsaquo;</div>
                <div data-crop="after"></div>`;
              row.querySelector(".lead").textContent = move.lead;
              row.querySelector(".line").textContent = move.line;
              fillRefs(row.querySelector(".refs"), move.refs ?? []);
              rows.append(row);
    
              // Both crops in a row share one height, so the pair reads as one comparison.
              const boxes = {};
              const regions = {};
              const notes = [];
              for (const side of ["before", "after"]) {
                const slot = row.querySelector(`[data-crop="${side}"]`);
                if (move[side] === undefined) {
                  const note = missingNote(side);
                  notes.push(note);
                  slot.append(note);
                  continue;
                }
                boxes[side] = cropBox(side);
                regions[side] = regionOf(panes[side], move[side]);
                slot.append(boxes[side]);
              }
              const width = Object.values(boxes)[0].clientWidth;
              const height = Math.min(
                CROP_MAX_HEIGHT,
                ...Object.values(regions).map((region) => (width * region.height) / region.width)
              );
              for (const side of Object.keys(boxes)) fitCrop(boxes[side], regions[side], width, height);
              for (const note of notes) note.style.height = `${Math.max(height, CROP_MIN_HEIGHT)}px`;
            }
          }
    
          /** The rect grown by its own share of context, clamped to the pane. In natural pixels. */
          function regionOf(img, rect) {
            const padX = ((rect.w / 100) * img.naturalWidth * CROP_CONTEXT) / 2;
            const padY = ((rect.h / 100) * img.naturalHeight * CROP_CONTEXT) / 2;
            const left = Math.max(0, (rect.x / 100) * img.naturalWidth - padX);
            const top = Math.max(0, (rect.y / 100) * img.naturalHeight - padY);
            return {
              left,
              top,
              width: Math.min(img.naturalWidth - left, (rect.w / 100) * img.naturalWidth + padX * 2),
              height: Math.min(img.naturalHeight - top, (rect.h / 100) * img.naturalHeight + padY * 2),
            };
          }
    
          /** Scales the pane image so the whole region fits the box, then centres it. */
          function fitCrop(box, region, width, height) {
            box.style.height = `${height}px`;
            const scale = Math.min(width / region.width, height / region.height);
            const offsetX = -region.left * scale + (width - region.width * scale) / 2;
            const offsetY = -region.top * scale + (height - region.height * scale) / 2;
            box.firstElementChild.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`;
          }
    
          function cropBox(side) {
            const box = document.createElement("div");
            box.className = "crop";
            const img = document.createElement("img");
            img.src = data.panes[side];
            box.append(img);
            return box;
          }
    
          function missingNote(side) {
            const note = document.createElement("div");
            note.className = "crop-none";
            note.textContent = side === "before" ? "Not in before" : "Not in after";
            return note;
          }
    
          /** "References: App · App", each app a link to its Mobbin screen, as in the report. */
          function fillRefs(el, refs) {
            if (refs.length === 0) return el.remove();
            el.append("References: ");
            refs.forEach((ref, i) => {
              if (i > 0) el.append(" · ");
              const a = document.createElement("a");
              a.href = ref.url;
              a.target = "_blank";
              a.rel = "noopener";
              a.textContent = ref.app;
              el.append(a);
            });
          }
    
          function sized(img) {
            if (img.complete && img.naturalWidth > 0) return Promise.resolve();
            return new Promise((resolve) => img.addEventListener("load", resolve, { once: true }));
          }
        </script>
      </body>
    </html>
    
  • skills/redesign-screen/assets/chrome/ios.css5.6 KB4677902e0066upstream ↗
    /*
      iOS system chrome for the redesign-screen shell: status bar, home indicator and
      keyboard, drawn rather than cropped, so every variation carries the same chrome.
    
      Our own drawing of the platform's chrome, not Apple's UI kit. It is the same in
      every run: 9:41, full signal, wifi and battery, whatever the input showed. Only
      the glyph colour follows the screen, through data-glyphs on .device.
    
      Sizes are points at a 393 pt wide phone; the capture's device scale factor turns
      them into pixels. The device has a width and no height: it is as tall as the
      screen's content, and the chrome keeps to its top and bottom edges.
    */
    
    .device {
      --safe-top: 54px;
      --safe-bottom: 34px;
      --keyboard-height: 0px;
      --glyph: #000;
      position: relative;
      overflow: hidden;
      width: var(--device-width, 393px);
      font-family: -apple-system, "SF Pro Text", system-ui, sans-serif;
    }
    .device[data-glyphs="light"] {
      --glyph: #fff;
    }
    .device[data-keyboard="light"],
    .device[data-keyboard="dark"] {
      --keyboard-height: 336px;
    }
    
    /* Chrome sits above the slot and never takes a click, so the slot cannot cover it. */
    .chrome {
      position: absolute;
      left: 0;
      right: 0;
      z-index: 10;
      pointer-events: none;
    }
    
    /* Status bar: two ears either side of the camera cut-out, each centring its content. */
    .status-bar {
      top: 0;
      display: flex;
      justify-content: space-between;
      height: var(--safe-top);
      color: var(--glyph);
    }
    .status-bar .ear {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      width: calc((100% - 126px) / 2);
      padding-top: 4px;
    }
    .status-bar .time {
      font-size: 17px;
      font-weight: 600;
      letter-spacing: -0.4px;
      line-height: 1;
    }
    .glyph {
      display: block;
      background: currentColor;
      -webkit-mask: var(--mask) center / contain no-repeat;
      mask: var(--mask) center / contain no-repeat;
    }
    .glyph.signal {
      width: 18px;
      height: 12px;
      --mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 12'%3E%3Crect x='0' y='7.5' width='3' height='4.5' rx='1'/%3E%3Crect x='5' y='5' width='3' height='7' rx='1'/%3E%3Crect x='10' y='2.5' width='3' height='9.5' rx='1'/%3E%3Crect x='15' y='0' width='3' height='12' rx='1'/%3E%3C/svg%3E");
    }
    .glyph.wifi {
      width: 17px;
      height: 12px;
      --mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 17 12' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M1.9 4.6a9.3 9.3 0 0 1 13.2 0'/%3E%3Cpath d='M4.6 7.3a5.5 5.5 0 0 1 7.8 0'/%3E%3Cpath d='M8.5 11.3 6.9 9.7a2.3 2.3 0 0 1 3.2 0z' fill='%23000' stroke-width='1.4' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    .glyph.battery {
      width: 27px;
      height: 13px;
      --mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 27 13'%3E%3Crect x='.5' y='.5' width='23' height='12' rx='3.8' fill='none' stroke='%23000' stroke-opacity='.4'/%3E%3Crect x='2' y='2' width='20' height='9' rx='2.5'/%3E%3Cpath d='M25 4.5v4c.8-.3 1.3-1.1 1.3-2s-.5-1.7-1.3-2z' fill-opacity='.4'/%3E%3C/svg%3E");
    }
    
    /* Home indicator, on the content, or on the keyboard when one is shown. */
    .home-indicator {
      bottom: 8px;
      left: 50%;
      right: auto;
      width: 134px;
      height: 5px;
      border-radius: 100px;
      background: var(--glyph);
      transform: translateX(-50%);
    }
    .device[data-keyboard="light"] .home-indicator {
      background: #000;
    }
    .device[data-keyboard="dark"] .home-indicator {
      background: #fff;
    }
    
    /* Keyboard: predictive bar, four rows of keys, then the globe and dictation row. */
    .keyboard {
      bottom: 0;
      display: none;
      height: var(--keyboard-height);
      padding: 0 3px;
      --kb-bg: #d4d6dd;
      --key: #fff;
      --key-alt: #acb1bb;
      --key-fg: #000;
      --key-shadow: #898a8d;
      background: var(--kb-bg);
      color: var(--key-fg);
    }
    .device[data-keyboard="dark"] .keyboard {
      --kb-bg: #2c2c2e;
      --key: #6b6b6b;
      --key-alt: #464646;
      --key-fg: #fff;
      --key-shadow: #000;
    }
    .device[data-keyboard="light"] .keyboard,
    .device[data-keyboard="dark"] .keyboard {
      display: block;
    }
    .keyboard .predictive {
      display: flex;
      align-items: center;
      height: 44px;
      font-size: 16px;
    }
    .keyboard .predictive span {
      flex: 1;
      text-align: center;
    }
    .keyboard .predictive span + span {
      border-left: 1px solid color-mix(in srgb, var(--key-fg) 20%, transparent);
    }
    .keyboard .row {
      display: flex;
      justify-content: center;
      gap: 6px;
      height: 42px;
      margin-bottom: 12px;
    }
    .keyboard .row.inset {
      padding: 0 19px;
    }
    .keyboard .key {
      display: flex;
      flex: 1;
      align-items: center;
      justify-content: center;
      max-width: 34px;
      border-radius: 5px;
      background: var(--key);
      box-shadow: 0 1px 0 var(--key-shadow);
      font-size: 22px;
      line-height: 1;
    }
    .keyboard .key.alt {
      max-width: 44px;
      background: var(--key-alt);
      font-size: 16px;
    }
    .keyboard .key.wide {
      max-width: 88px;
    }
    .keyboard .key.space {
      max-width: none;
      flex: 5;
      font-size: 16px;
    }
    .keyboard .bottom {
      display: flex;
      justify-content: space-between;
      padding: 2px 22px 0;
    }
    .keyboard .bottom .glyph {
      width: 26px;
      height: 26px;
      color: var(--key-fg);
    }
    .glyph.globe {
      --mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6'%3E%3Ccircle cx='12' cy='12' r='9.5'/%3E%3Cpath d='M2.5 12h19M12 2.5c2.6 2.6 3.9 5.8 3.9 9.5s-1.3 6.9-3.9 9.5M12 2.5C9.4 5.1 8.1 8.3 8.1 12s1.3 6.9 3.9 9.5'/%3E%3C/svg%3E");
    }
    .glyph.mic {
      --mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'%3E%3Crect x='9' y='2.5' width='6' height='12' rx='3'/%3E%3Cpath d='M5.5 11.5a6.5 6.5 0 0 0 13 0M12 18v3.5'/%3E%3C/svg%3E");
    }
    
  • skills/redesign-screen/assets/reference-board.html14 KBfe2efbde7d4fupstream ↗
    <!doctype html>
    <!--
      Reference board for the redesign-screen skill. The rules for what goes on it are
      in references/reference-board.md.
    
      Fill in the JSON in #board-data and change nothing else. The layout and the
      type are already set here, so a run writes data, not CSS.
    
      Deliver it either way:
      - as a PNG: open this file in a browser and capture the .board element at twice
        the pixel scale, after the images have loaded. A capture tool that cannot set
        a device pixel ratio gets the same detail from --board-zoom: 2 below.
      - as itself: run scripts/embed-images.py on it first, which puts the screen
        images inside the file, so it opens on its own and goes without them. The
        app names link to their Mobbin screens only here, not in the PNG.
    
      "image" is the file downloaded from the screen's image_url, never the inline
      preview. "also" is left out unless the screen backs a later problem too.
      "label" is left out after a targeted run, so groups number 1, 2, 3; after an
      explore run it is the variation's letter.
    
      Screens sit five to a row. "columns" is left out for phone screens; set it to 2
      for web screens, so they stay readable.
    
      "collections" lists the user's collections the run read, each { "name", "url" }
      with the collection's mobbin_url, as chips on the title line. Leave it out when
      the run read no collection, such as a run built from a search alone.
    -->
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <title>Mobbin references</title>
        <style>
          /* Mobbin design system tokens, verbatim from ux-audit-tool's shared/design-system.css. */
          :root {
            --gray-0: #ffffff;
            --gray-50: #fafafa;
            --gray-100: #f5f5f5;
            --gray-150: #ededed;
            --gray-200: #e5e5e5;
            --gray-300: #d4d4d4;
            --gray-400: #a3a3a3;
            --gray-500: #737373;
            --gray-600: #525252;
            --gray-700: #404040;
            --gray-800: #262626;
            --gray-900: #171717;
            --gray-950: #0a0a0a;
            --bg: var(--gray-0);
            --bg-subtle: var(--gray-50);
            --bg-muted: var(--gray-100);
            --fg1: var(--gray-950);
            --fg2: var(--gray-600);
            --fg3: var(--gray-500);
            --fg-muted: var(--gray-400);
            --border: var(--gray-200);
            --border-strong: var(--gray-300);
            --border-subtle: var(--gray-150);
            --success: #1a7f46;
            --warning: #b45309;
            --danger: #b42318;
            --info: #1c5fd4;
            --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
            --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
            --fs-11: 11px;
            --fs-12: 12px;
            --fs-13: 13px;
            --fs-14: 14px;
            --fs-15: 15px;
            --fs-24: 24px;
            --ls-normal: -0.005em;
            --ls-tight: -0.02em;
            --ls-widest: 0.12em;
            --sp-2: 8px;
            --sp-3: 12px;
            --sp-4: 16px;
            --sp-5: 20px;
            --sp-6: 24px;
            --sp-8: 32px;
            --sp-10: 40px;
            --r-sm: 5px;
            --r-md: 8px;
            --r-lg: 12px;
            --r-xl: 16px;
    
            --board-width: 1320px;
            --board-zoom: 1; /* 2 doubles every pixel, for a capture tool with no scale factor of its own. */
          }
          :root[data-theme="dark"] {
            --bg: var(--gray-950);
            --bg-subtle: var(--gray-900);
            --bg-muted: var(--gray-800);
            --fg1: var(--gray-50);
            --fg2: var(--gray-400);
            --fg3: var(--gray-500);
            --fg-muted: var(--gray-600);
            --border: var(--gray-800);
            --border-strong: var(--gray-700);
            --border-subtle: var(--gray-800);
            --success: #46c485;
            --warning: #e0a34a;
            --danger: #f2a099;
            --info: #7aa7f0;
          }
    
          * {
            box-sizing: border-box;
          }
          html,
          body {
            margin: 0;
            background: var(--bg-subtle);
          }
          /* Centred when the file is opened; the capture shoots .board alone, so it is unaffected. */
          body {
            display: flex;
            justify-content: safe center;
            padding: var(--sp-10) var(--sp-4);
          }
          .board {
            flex: none;
          }
          .board {
            zoom: var(--board-zoom);
            width: var(--board-width);
            padding: var(--sp-10);
            background: var(--bg-subtle);
            color: var(--fg1);
            font-family: var(--font-sans);
            font-size: var(--fs-15);
            line-height: 1.5;
            letter-spacing: var(--ls-normal);
            -webkit-font-smoothing: antialiased;
          }
    
          .docmeta {
            display: flex;
            justify-content: space-between;
            gap: var(--sp-4);
            margin-bottom: var(--sp-6);
            color: var(--fg-muted);
            font-family: var(--font-mono);
            font-size: var(--fs-11);
          }
          .docmeta .stamp {
            letter-spacing: var(--ls-widest);
            text-transform: uppercase;
          }
    
          h1 {
            margin: 0;
            font-size: var(--fs-24);
            font-weight: 600;
            letter-spacing: var(--ls-tight);
            line-height: 1.25;
          }
          .subtitle {
            margin: var(--sp-2) 0 0;
            color: var(--fg2);
            font-size: var(--fs-14);
          }
          .head {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            gap: var(--sp-8);
          }
          .heading {
            flex: 1;
            min-width: 0;
          }
          /* Collections the run read, on the title line. The width caps how many chips share a
             line; later lines wrap under the chips, right-aligned. */
          .collections {
            display: flex;
            flex-wrap: wrap;
            justify-content: flex-end;
            align-items: center;
            gap: var(--sp-2);
            max-width: 580px;
            margin-top: 1px; /* Centres the first line on the title's 30px line. */
            color: var(--fg3);
            font-size: var(--fs-13);
          }
          .collections[hidden] {
            display: none;
          }
          .collections-label {
            white-space: nowrap;
          }
          .chip {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            max-width: 240px;
            height: 28px;
            padding: 0 10px 0 12px;
            border: 1px solid var(--border);
            border-radius: 999px;
            background: var(--bg);
            color: var(--fg1);
            font-weight: 500;
            text-decoration: none;
          }
          .chip-name {
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }
          .chip svg {
            flex: none;
            width: 10px;
            height: 10px;
            color: var(--fg2);
          }
    
          .group {
            margin-top: var(--sp-8);
            padding-top: var(--sp-8);
            border-top: 1px solid var(--border);
          }
          .group-head {
            display: flex;
            gap: var(--sp-3);
            align-items: flex-start;
            margin-bottom: var(--sp-5);
          }
          .num {
            flex: none;
            display: flex;
            align-items: center;
            justify-content: center;
            width: 22px;
            height: 22px;
            margin-top: 1px;
            border-radius: 50%;
            background: var(--fg1);
            color: var(--bg);
            font-family: var(--font-mono);
            font-size: var(--fs-12);
            font-weight: 700;
            line-height: 1;
          }
          .lead {
            margin: 0;
            font-size: var(--fs-15);
            font-weight: 600;
          }
          .problem {
            margin: 0;
            color: var(--fg2);
            font-size: var(--fs-13);
          }
    
          .cards {
            display: grid;
            grid-template-columns: repeat(var(--columns, 5), 1fr);
            gap: var(--sp-4);
            align-items: start;
          }
          .card {
            padding: 6px;
            background: var(--bg);
            border: 1px solid var(--border);
            border-radius: var(--r-lg);
          }
          .card img {
            display: block;
            width: 100%;
            height: auto;
            border-radius: var(--r-md);
            background: var(--bg-muted);
          }
          .caption {
            padding: var(--sp-3) 6px 6px;
          }
          .app {
            color: var(--info);
            font-size: var(--fs-14);
            font-weight: 600;
            text-decoration: none;
          }
          .take {
            margin: 2px 0 0;
            color: var(--fg2);
            font-size: var(--fs-13);
            line-height: 1.45;
          }
          .also {
            margin: 6px 0 0;
            color: var(--fg3);
            font-size: var(--fs-12);
            font-style: italic;
          }
        </style>
      </head>
      <body>
        <script type="application/json" id="board-data">
          {
            "theme": "light",
            "meta": "iOS paywall, 2 problems, 3 references",
            "title": "Mobbin references used in the paywall redesign",
            "subtitle": "Grouped by the problem each change answered.",
            "groups": [
              {
                "lead": "The trial button is in the first view.",
                "problem": "The main action sits below the fold.",
                "screens": [
                  {
                    "app": "Headspace",
                    "url": "https://mobbin.com/screens/example-1",
                    "image": "ref-1.png",
                    "take": "Trial button pinned above the plan list",
                    "also": "also problem 2"
                  },
                  {
                    "app": "Calm",
                    "url": "https://mobbin.com/screens/example-2",
                    "image": "ref-2.png",
                    "take": "One plan card, button directly under it"
                  }
                ]
              },
              {
                "lead": "The charge is stated before the choice.",
                "problem": "The price after the trial is hidden.",
                "screens": [
                  {
                    "app": "Strava",
                    "url": "https://mobbin.com/screens/example-3",
                    "image": "ref-3.png",
                    "take": "Timeline, then one plan card carrying both prices"
                  }
                ]
              }
            ]
          }
        </script>
    
        <div class="board">
          <div class="docmeta">
            <span class="stamp">Mobbin · redesign-screen</span><span id="meta"></span>
          </div>
          <div class="head">
            <div class="heading">
              <h1 id="title"></h1>
              <p class="subtitle" id="subtitle"></p>
            </div>
            <div class="collections" id="collections" hidden></div>
          </div>
          <div id="groups"></div>
        </div>
    
        <template id="group-template">
          <section class="group">
            <div class="group-head">
              <span class="num"></span>
              <div>
                <p class="lead"></p>
                <p class="problem"></p>
              </div>
            </div>
            <div class="cards"></div>
          </section>
        </template>
    
        <template id="card-template">
          <figure class="card" style="margin: 0">
            <img alt="" />
            <figcaption class="caption">
              <a class="app" target="_blank" rel="noopener"></a>
              <p class="take"></p>
              <p class="also"></p>
            </figcaption>
          </figure>
        </template>
    
        <script>
          const data = JSON.parse(document.getElementById("board-data").textContent);
          document.documentElement.dataset.theme = data.theme ?? "light";
          document.getElementById("meta").textContent = data.meta ?? "";
          document.getElementById("title").textContent = data.title;
          document.getElementById("subtitle").textContent = data.subtitle ?? "";
          fillCollections(document.getElementById("collections"), data.collections ?? []);
    
          const groupTemplate = document.getElementById("group-template");
          const cardTemplate = document.getElementById("card-template");
          const groups = document.getElementById("groups");
          if (data.columns) groups.style.setProperty("--columns", data.columns);
    
          data.groups.forEach((group, i) => {
            const section = groupTemplate.content.cloneNode(true);
            section.querySelector(".num").textContent = group.label ?? i + 1;
            section.querySelector(".lead").textContent = group.lead;
            section.querySelector(".problem").textContent = group.problem;
            const cards = section.querySelector(".cards");
            for (const screen of group.screens) cards.append(buildCard(screen));
            groups.append(section);
          });
    
          function buildCard(screen) {
            const card = cardTemplate.content.cloneNode(true);
            const img = card.querySelector("img");
            img.src = screen.image;
            img.alt = `${screen.app} screen`;
            const app = card.querySelector(".app");
            app.href = screen.url;
            app.textContent = screen.app;
            card.querySelector(".take").textContent = screen.take;
            const also = card.querySelector(".also");
            if (screen.also) also.textContent = screen.also;
            else also.remove();
            return card;
          }
    
          /** One chip per collection, each a link to it on Mobbin; a long name is cut with an ellipsis. */
          function fillCollections(el, collections) {
            if (collections.length === 0) return;
            el.hidden = false;
            const label = document.createElement("span");
            label.className = "collections-label";
            label.textContent = "Collections referenced:";
            el.append(label);
            for (const collection of collections) {
              const chip = document.createElement("a");
              chip.className = "chip";
              chip.href = collection.url;
              chip.target = "_blank";
              chip.rel = "noopener";
              chip.title = collection.name;
              const name = document.createElement("span");
              name.className = "chip-name";
              name.textContent = collection.name;
              chip.append(name);
              chip.insertAdjacentHTML(
                "beforeend",
                '<svg viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 7.5 7.5 2.5M3.5 2.5h4v4" /></svg>'
              );
              el.append(chip);
            }
          }
        </script>
      </body>
    </html>
    
  • skills/redesign-screen/assets/shell.html4.0 KB2d0680135211upstream ↗
    <!doctype html>
    <!--
      Mockup shell for the redesign-screen skill's explore mode. The rules are in
      references/variations.md.
    
      The lead agent copies this file and chrome/ once, sets the device and fills
      the slot with the baseline: the frozen app frame outside the region, and the
      region itself between the two region markers. Every variation is a copy of that
      file with only the part between the region markers changed, plus data-glyphs on
      .device when its top background needs the other glyph colour.
    
      On .device:
      - data-platform: "ios" draws the chrome; "web" draws none.
      - data-glyphs: "dark" for a light screen top, "light" for a dark one.
      - data-keyboard: "none", "light" or "dark", from the state the baseline shows.
      - style: --device-width, the input's width in points (iOS) or CSS pixels (web).
        There is no height to set: the screen is as tall as its content, so a
        variation that drops parts is shorter and one that adds parts is longer.
    
      The slot is padded clear of the status bar, the home indicator and the keyboard,
      and carries the screen's background, so the chrome draws over it. The region's
      CSS goes in its own style block, scoped to .slot, and never styles .chrome.
    
      Capture .device at the input's scale factor (3 for a modern iPhone screenshot).
    -->
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <title>Mockup</title>
        <link rel="stylesheet" href="chrome/ios.css" />
        <style>
          html,
          body {
            margin: 0;
          }
          .device[data-platform="web"] .chrome {
            display: none;
          }
          .device[data-platform="web"] {
            --safe-top: 0px;
            --safe-bottom: 0px;
          }
          .slot {
            position: relative;
            z-index: 0;
            padding: var(--safe-top) 0 calc(var(--safe-bottom) + var(--keyboard-height));
          }
        </style>
      </head>
      <body>
        <div
          class="device"
          data-platform="ios"
          data-glyphs="dark"
          data-keyboard="none"
          style="--device-width: 393px"
        >
          <main class="slot">
            <!-- The frozen app frame, as the baseline has it. -->
    
            <!-- region:start -->
            <style>
              .slot .region {
              }
            </style>
            <div class="region"></div>
            <!-- region:end -->
    
            <!-- The frozen app frame, continued. -->
          </main>
    
          <div class="chrome status-bar" aria-hidden="true">
            <div class="ear"><span class="time">9:41</span></div>
            <div class="ear">
              <span class="glyph signal"></span><span class="glyph wifi"></span
              ><span class="glyph battery"></span>
            </div>
          </div>
    
          <div class="chrome keyboard" aria-hidden="true">
            <div class="predictive"><span>I</span><span>The</span><span>I'm</span></div>
            <div class="row">
              <span class="key">q</span><span class="key">w</span><span class="key">e</span
              ><span class="key">r</span><span class="key">t</span><span class="key">y</span
              ><span class="key">u</span><span class="key">i</span><span class="key">o</span
              ><span class="key">p</span>
            </div>
            <div class="row inset">
              <span class="key">a</span><span class="key">s</span><span class="key">d</span
              ><span class="key">f</span><span class="key">g</span><span class="key">h</span
              ><span class="key">j</span><span class="key">k</span><span class="key">l</span>
            </div>
            <div class="row">
              <span class="key alt">⇧</span><span class="key">z</span><span class="key">x</span
              ><span class="key">c</span><span class="key">v</span><span class="key">b</span
              ><span class="key">n</span><span class="key">m</span><span class="key alt">⌫</span>
            </div>
            <div class="row">
              <span class="key alt">123</span><span class="key space">space</span
              ><span class="key alt wide">return</span>
            </div>
            <div class="bottom"><span class="glyph globe"></span><span class="glyph mic"></span></div>
          </div>
    
          <div class="chrome home-indicator" aria-hidden="true"></div>
        </div>
      </body>
    </html>
    
  • skills/redesign-screen/assets/variations.html16 KB0b724049c8a9upstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

  • README.mdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

Plain-text copy, no login: /skill-bundles/mobbin-skills-skills-redesign-screen.md

// REPO STATS

64 stars