Article 12(7) asks for it
The GDPR contemplates exactly this scheme: information "may be provided in combination with standardised icons", and "where the icons are presented electronically they shall be machine-readable". Three artefacts make that true here: identifiers on every icon, a label markup convention, and a policy record format that captures the operator's selections so that software can rebuild the label — or compare two sites — without reading prose. A parser for that record is phase 3.
1. Identifiers
Every practice has a stable id of the form category.slug (e.g. retention.fixed); every category has cat.<category>. Ids never change meaning between versions; deprecated ids are kept and marked. Every SVG carries its id in a data-vp attribute and its name in <title>. The full list, with descriptions, policy text and legal references, is the manifest vp-icons.json.
2. Label markup
<div class="vp-label" data-vp-version="1.2">
<section data-vp-cat="sharing">
<img src="icons/cat.sharing.svg" alt="Recipients">
<h4>Recipients & disclosure</h4>
<ul>
<li><img src="icons/sharing.processors.svg"
data-vp="sharing.processors" alt="">
Service providers</li>
<li><img src="icons/sharing.not-sold.svg"
data-vp="sharing.not-sold" alt="">
Personal information not sold</li>
</ul>
</section>
</div>A label is a list of sections, one per category present, each holding the practice icons with captions. The builder's "Copy label HTML" emits exactly this with a short inline stylesheet. A crawler needs only the data-vp-cat and data-vp attributes.
Icons are drawn on a 64×64 grid, monoline, and take their colour from currentColor; the CSS variables --vp-bg and --vp-fg override badge and glyph colours, which is how the category badges are tinted.
3. The policy record: vp-policy.json
The generator's primary output. It records what the operator selected and why, in enough detail to regenerate the label and the policy skeleton. The render block is optional and exists so that the widget can draw the label without fetching the icon set. Copy a live example from the sample policy page.
| Field | Meaning |
|---|---|
version, generated, generator, manifest | Format version (semver), ISO date, the tool that produced the record, the icon manifest version it was built against. |
operator | name, policy_url (the full policy the widget links to), contact, optional dpo, representative. |
jurisdictions | Modules switched on: any of EU, UK, AU, US-CA. |
practices | Ordered list of practice ids that apply. This alone is enough to draw the label. |
basis_by_purpose | Map of each purpose.* id to the basis.* id relied on; a purpose may list more than one basis with the data groups each covers. |
legitimate_interests | Where legitimate interests are relied on: the interests identified (from a controlled list plus free text), per purpose, and whether an assessment was recorded. |
data_by_purpose | Map of purpose id → data ids used for it (the purpose/data matrix). |
retention | Map of data id or group → {practice, period, criteria}. |
transfers | List of {country, recipient_type, safeguard}, safeguard being a transfer.* id. |
variables | Every placeholder value the operator entered, keyed <practice id>.<var> — substituted into the policy text. |
compliance | Self-confirmation checklist: for each process requirement (Arts 25, 28, 30, 32, 33, 35, 37…) whether it is met, how, and the date. |
render | Optional. Categories (id, name, colour, glyph, order) and the selected icons (name, description, glyph) so a widget can draw everything offline. |
4. The widget
<div id="privacy-badge"></div>
<script src="https://…/vp-widget.js"></script>
<script>
VPWidget.mount({
target: '#privacy-badge',
policy: '/vp-policy.json', // or the object
theme: { bg:'#1f2125', fg:'#ffffff',
accent:'#F5A742', radius:'12px' },
label: 'Privacy at a glance'
});
</script>A single script with no dependencies. It draws a small badge for a site footer; clicking it opens a floating panel that shows the label as accordions — one per category, with the practice icons in miniature on the closed row and captions and descriptions when expanded — and a link to the full policy. Colours, radius and label text are configurable; the panel is rendered in a shadow root so the host page's CSS does not leak in. Try it on the generate page.
5. Phase 3: a parser
Because the record is data rather than prose, a parser is small: validate against the schema, resolve ids against the manifest version named in the file, and emit a label, a comparison against another record, or a checklist of missing Article 13 items. Candidate homes are a browser extension that shows the label for the current site, a comparison page ("how do these three shops differ?"), and a supervisory-authority sweep tool. The format also maps naturally onto the W3C Data Privacy Vocabulary, which would let records interoperate with other consent and privacy tooling.