ContentScoreBar
OrganismRule-based content quality score with Good ≥70 / Fair ≥40 / Poor <40 tier system. Each rule shown as a chip with passed/total count. role="progressbar" + aria-valuenow.
Quality score
Good
80%
Min 20 charsHas numberHas uppercaseHas keywordMin 5 words
4 / 5 rules passed
<%# Evaluate rules in your Express route, then pass score + results. %>
<%
// const value = req.body.content || '';
// const rules = [
// { label: 'Min 20 chars', check: function (v) { return v.length >= 20; }, points: 20 },
// { label: 'Has keyword', check: function (v) { return /react/i.test(v); }, points: 20, hint: 'Include "React"' },
// // ...
// ];
// let earned = 0, total = 0;
// const results = rules.map(function (r) {
// const pass = r.check(value); if (pass) earned += r.points; total += r.points;
// return { label: r.label, pass: pass, hint: r.hint };
// });
// const score = total > 0 ? Math.round((earned / total) * 100) : 0;
%>
<%- include('modules/ui/ContentScoreBar', {
label: 'Quality score',
score: score,
results: results
}) %>
Good (100%)
Good
100%
Rule 1Rule 2Rule 3Rule 4Rule 5
5 / 5 rules passed
Fair (60%)
Fair
60%
Rule 1Rule 2Rule 3Rule 4Rule 5
3 / 5 rules passed
Poor (20%)
Poor
20%
Rule 1Rule 2Rule 3Rule 4Rule 5
1 / 5 rules passed
<%# Good tier (score ≥ 70) %>
<%- include('modules/ui/ContentScoreBar', { score: 100, label: 'Good (100%)', results: allPassRules }) %>
<%# Fair tier (40 ≤ score < 70) %>
<%- include('modules/ui/ContentScoreBar', { score: 60, label: 'Fair (60%)', results: halfPassRules }) %>
<%# Poor tier (score < 40) %>
<%- include('modules/ui/ContentScoreBar', { score: 20, label: 'Poor (20%)', results: onePassRules }) %>
Password strength
Fair
50%
Min 8 charsUppercaseNumberSpecial char
2 / 4 rules passed
<%# Server-side password rule evaluation (mirror of NextJS preview). %>
<%
// const rules = [
// { label: 'Min 8 chars', check: function (v) { return v.length >= 8; }, points: 25 },
// { label: 'Uppercase', check: function (v) { return /[A-Z]/.test(v); }, points: 25 },
// { label: 'Number', check: function (v) { return /\d/.test(v); }, points: 25 },
// { label: 'Special char', check: function (v) { return /[^A-Za-z0-9]/.test(v); }, points: 25 },
// ];
%>
<%- include('modules/ui/ContentScoreBar', {
label: 'Password strength',
score: score,
results: results
}) %>
<%
// ContentScoreBar — server-rendered, presentational variant.
// Props:
// score : number (0–100) — precomputed score
// results : array of { label, pass, hint? } — precomputed rule results
// label : optional uppercase eyebrow label
// className: optional extra classes
var _score = (typeof locals.score === 'number') ? locals.score : 0;
var _results = Array.isArray(locals.results) ? locals.results : [];
var _label = locals.label || '';
var _className = locals.className || '';
if (isNaN(_score)) _score = 0;
if (_score < 0) _score = 0;
if (_score > 100) _score = 100;
var tier = (_score >= 70) ? 'great' : (_score >= 40) ? 'ok' : 'poor';
var tierMap = {
great: { bar: 'bg-success', text: 'text-success-fg', bg: 'bg-success-subtle', border: 'border-success', dot: 'bg-success', label: 'Good' },
ok: { bar: 'bg-warning', text: 'text-warning-fg', bg: 'bg-warning-subtle', border: 'border-warning', dot: 'bg-warning', label: 'Fair' },
poor: { bar: 'bg-error', text: 'text-error-fg', bg: 'bg-error-subtle', border: 'border-error', dot: 'bg-error', label: 'Poor' },
};
var t = tierMap[tier];
var passCount = 0;
for (var pi = 0; pi < _results.length; pi++) { if (_results[pi].pass) passCount++; }
var ariaLabelText = (_label || 'Content score') + ': ' + _score + '%';
%>
<% if (_label) { %>
<%= _label %>
<% } %>
<%= t.label %>
<%= _score %>%
<% if (_results.length > 0) { %>
<% _results.forEach(function(r){
var pillClass = r.pass
? (t.bg + ' ' + t.text + ' border ' + t.border)
: 'bg-surface-sunken text-text-disabled border border-border';
%>
title="<%= r.hint %>"<% } %>
class="inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium cursor-default select-none transition-colors <%= pillClass %>"
>
<% if (r.pass) { %><% } %>
<%= r.label %>
<% }); %>
<%= passCount %> / <%= _results.length %> rules passed
<% } %>