webdevpuneet.com

Profile Completeness Card — Weighted Progress with a Genuine Next-Best-Action Suggestion

Profile Completeness Card — Weighted Progress with Next-Best-Action · Cards · Plain HTML, CSS & JS · Live preview

CategoryCards

Copy & paste

Source Code

<div class="demo">
  <div class="pc-card">
    <div class="pc-header">
      <svg class="pc-ring" viewBox="0 0 64 64" width="56" height="56">
        <circle cx="32" cy="32" r="27" fill="none" stroke="#f1f5f9" stroke-width="7" />
        <circle id="pcRingFill" cx="32" cy="32" r="27" fill="none" stroke="#6366f1" stroke-width="7" stroke-linecap="round" transform="rotate(-90 32 32)" />
      </svg>
      <div>
        <p class="pc-title">Profile strength</p>
        <p class="pc-percent" id="pcPercent">0%</p>
      </div>
    </div>

    <ul class="pc-list" id="pcList"></ul>

    <div class="pc-next" id="pcNext"></div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Weighted completion percentage reflects genuine field importance, not a flat "N of M items" fraction
Next-best-action suggestion always highlights the highest-impact incomplete item, not simply the first one in list order
SVG progress ring uses correct stroke-dasharray/stroke-dashoffset math with a -90deg rotation for a conventional top-start orientation
Entire card (ring, percentage, checklist, and suggestion) recomputes from the same source array on every render, guaranteeing internal consistency
Each checklist item displays its own weight contribution, making the weighting system transparent to the user
Both the checklist itself and a dedicated action button can mark an item complete, sharing one click handler
Distinct celebratory state once every item is complete, rather than an empty or unchanged next-action panel

About this UI Snippet

Weighted Profile Completeness — Why "6 of 8 Done" Is the Wrong Percentage

Screenshot of the Profile Completeness Card — Weighted Progress with Next-Best-Action snippet rendered live

A profile completeness indicator built as a flat fraction — "6 of 8 fields filled in, so 75%" — treats every field as equally valuable, which is rarely true. A profile photo genuinely matters more to how complete and trustworthy a profile *feels* than an optional personal tagline does. This snippet weights each field by its actual importance, so the displayed percentage reflects genuine usefulness rather than a naive item count.

Weighted completion, not a flat fraction

Each item in ITEMS carries its own weight (photo: 25, bio: 20, skills: 20, role: 15, social: 10, tagline: 10 — summing to 100). computeWeightedPercent() sums the weights of only the *completed* items and divides by the total possible weight, rather than counting "how many of the 6 items are done" and dividing by 6. The practical difference: completing the photo and bio (45 combined weight) genuinely represents more real progress than completing the social link and tagline (20 combined weight), even though both pairs are "2 out of 6 items" in a naive flat count — the weighted percentage correctly reflects that difference, a flat fraction would not.

The "next best action" is the highest-impact incomplete item, not the first one

findNextBestAction() filters down to incomplete items and then finds the one with the *highest weight* among them — not simply incomplete[0], the first incomplete item in list order. This is the detail that makes the suggestion genuinely useful rather than arbitrary: recommending "add a personal tagline" (worth only 10%) when "add a profile photo" (worth 25%) is also still incomplete would be actively bad advice, pointing the user toward the *least* impactful thing they could do next rather than the most.

The progress ring's stroke-dashoffset math

The SVG ring uses the classic stroke-dasharray/stroke-dashoffset technique for a circular progress indicator: stroke-dasharray is set once to the circle's full circumference, and stroke-dashoffset is set to circumference * (1 - percent/100) — at 0%, the offset equals the full circumference (nothing visible); at 100%, the offset is 0 (the full circle drawn). The circle is also rotated -90 degrees so progress visually starts from the top (12 o'clock) rather than the default 3 o'clock starting point SVG circles use, matching the conventional orientation for this kind of progress ring.

Marking an item done recalculates everything from scratch

The single click handler, whether triggered from the checklist itself or the "Do this" button, does nothing more than flip one item's done flag to true and call render() again — the weighted percentage, the ring fill, the checklist display, and the next-best-action suggestion are all *recomputed fresh* from the current ITEMS array on every render, rather than being incrementally patched. This keeps the whole card's state trivially consistent: there's no way for the ring's percentage to disagree with the checklist's checked items, because both are derived from the same array on every single render call.

Step by step

How to Use

  1. 1
    Observe the initial weighted percentageWith photo and bio complete (45 combined weight out of 100), the ring shows 45% — not 33%, which a flat "2 of 6 items" count would incorrectly show.
  2. 2
    Check the "Biggest impact next" suggestionIt recommends the highest-weight remaining item (skills, worth 20%) — not simply the next item in the checklist's visual order.
  3. 3
    Click "Do this" or click a checklist item directlyMarks that item complete, and the ring, percentage, checklist, and next-best-action suggestion all update together from a single recalculation.
  4. 4
    Complete every itemThe next-action panel switches to a "profile complete" celebratory state once no incomplete items remain.
  5. 5
    Adjust ITEMS' weights for your own use caseChange which fields matter most by adjusting their weight values — the percentage, ring, and next-best-action logic all adapt automatically with no other code changes.

Real-world uses

Common Use Cases

ONBOARDING
User profile completion prompts
Social platforms, professional networks, and SaaS onboarding flows nudging users to complete their profile with a genuinely prioritized suggestion.
HR
Employee or candidate profile strength
HR platforms and job-seeking tools showing how complete a candidate or employee profile is, weighted by which fields matter most to hiring outcomes.
MARKETPLACE
Seller or vendor profile completeness
Marketplace platforms encouraging sellers to complete high-impact profile fields (verified payment, photos) over low-impact ones.
CRM
Contact or lead record completeness
CRM tools showing how complete a contact record is, weighted toward the fields most useful for sales or support workflows.

Got questions?

Frequently Asked Questions

A flat item count treats every field as equally valuable, which rarely reflects reality — a profile photo matters more to a profile's actual usefulness than an optional tagline. Weighting each field by importance means the displayed percentage genuinely represents how complete and useful the profile is, not just how many boxes happen to be checked.

findNextBestAction() filters to only the currently incomplete items and picks whichever one has the highest weight among them — not simply the first incomplete item in the checklist's visual order. This ensures the suggestion always points toward the single action that would improve the completeness percentage the most.

findNextBestAction() returns null when no incomplete items remain, and the next-action panel switches to a distinct celebratory "profile complete" state instead of showing an empty or broken suggestion.

It uses the standard stroke-dasharray/stroke-dashoffset technique: stroke-dasharray is set once to the circle's full circumference, and stroke-dashoffset is calculated as circumference times (1 minus the percent complete), so a higher percentage results in a smaller offset and more of the circle's stroke visibly drawn.

SVG circles start drawing their stroke from the 3 o'clock position by default. Rotating -90 degrees shifts the starting point to 12 o'clock, matching the conventional orientation most circular progress indicators use.

No — render() recomputes the weighted percentage, the ring's fill, the checklist display, and the next-best-action suggestion all fresh from the same ITEMS array every single time it runs, so there is no possible state where any of these pieces could disagree with the underlying data.