CSS hooks (Angular)
This page lists what a stylesheet can select in the output of <ds-odrl-prose>. The Angular package renders the read-only output of OdrlProse and nothing else, so the hooks are the read-only ones. There is no editor in the Angular package; the editor hooks are on the Yew CSS hooks page and do not apply here.
The repository states no CSS stability policy. The split between Stable and Internal on the Yew page is a recommendation, and every class below is Stable in that split. The table is checked against prose-yew/src/lib.rs, the file that writes this markup: a test fails when a class is missing here or when this page names one the file does not write.
The tree under the host element is:
ds-odrl-prose the Angular host element
article.ds-prose one root; articleClass is added here
section.prose-policy one per policy
h3, p, ul.prose-notes, ol.prose-rules > li.prose-rule.prose-permission | .prose-prohibition | .prose-obligation
p.prose-rule-sentence
p.prose-label, ul conditions, limits, follow-up rules
details.prose-glossary only with a profile that explains a term the policy uses
aside.prose-warnings only when something was not rendered
section.prose-violations only with validation and showViolations
article.ds-prose > p.prose-error instead of the policy, when the JSON cannot be read
p.prose-error instead of everything above, when the wasm cannot be loaded; outside article.ds-prose
The load error is a p.prose-error that is a direct child of ds-odrl-prose, outside article.ds-prose. Select it as ds-odrl-prose > .prose-error. The rule .ds-prose .prose-error does not match it, so prose.css has a second rule for it.
Stable hooks: read-only renderer #
These classes are written by OdrlProse (Yew), by ProseHandle (the wasm bridge) and by <ds-odrl-prose> (Angular). OdrlProseView in read mode writes the same names. The Rust source is prose-yew/src/lib.rs; the output is pinned by the snapshots in prose-yew/tests/snapshots/.
| Selector | Element | What it styles |
|---|---|---|
.ds-prose | article | The root. The class prop, or articleClass in Angular, is appended here. |
.prose-policy | section | One policy: an h3, an intro p, then the parts below. |
.prose-notes | ul | Notes about the policy, only when there are some. |
.prose-rules | ol | The rules of one policy. |
.prose-rule | li | One rule. A duty, remedy or consequence nested under a rule is also a li.prose-rule. |
.prose-permission, .prose-prohibition, .prose-obligation | li | The kind of the rule, next to .prose-rule. A duty is a .prose-obligation. |
.prose-rule-sentence | p | The sentence of a rule. |
.prose-reference | a | The IRI of a constraint the policy refers to and does not declare, in "the constraint defined at … holds", in a condition or in a rule's sentence (a target's or a party's refinement). A link only when the host lists the IRI in links (Yew), as the site's catalog does for IRIs that answer HTTP 200; otherwise the IRI is plain text. The wasm bridge and Angular pass no links, so they do not write it. |
.prose-iri | span, a | A party, an asset or a value the policy names by an IRI, in an intro, a rule's sentence or a condition. It shows the label the document declares for the IRI (rdfs:label, dct:title, skos:prefLabel or schema:name), else the IRI's last segment, and keeps the full IRI in title. An a with href only when the IRI is http(s) and the host lists it in links (Yew); otherwise a span. |
.prose-label | p | A lead-in such as "Applies only if:", and the heading of the warnings block. |
.prose-warnings | aside | The "Not rendered" block: parts of the policy the renderer did not turn into sentences. |
.prose-error | p[role="alert"] | Replaces the policy when the JSON cannot be read. It sits inside article.ds-prose. The Angular component also writes it when the wasm module cannot be loaded; that one is a child of ds-odrl-prose, outside article.ds-prose, and .ds-prose .prose-error does not match it. Select ds-odrl-prose > .prose-error. |
.prose-glossary | details[open] | Terms of the loaded profile that the policy uses, after the policy. At most 30 entries. |
.prose-note, .prose-kind-of, .prose-implies, .prose-deprecated, .prose-from | dd | One line of a glossary entry: usage note, "A kind of", "Implies", deprecation, source profile. |
.prose-violations | section | Validation findings and the verdict, after the policies. Written only with a validator and the findings switched on. |
.prose-violations-status | p[role="status"] | The verdict line. Written only for a strict validation. |
.prose-conforms, .prose-incomplete, .prose-violations-found | p | The verdict, next to .prose-violations-status: conforms, cannot be certified, does not conform. |
.prose-violations-list | ul | The findings. |
.prose-violation | li | One finding. |
.prose-violation-error, .prose-violation-warning, .prose-violation-info | li | The severity of a finding, next to .prose-violation. |
.prose-rule-id | code | The rule id inside a finding, for example prose.datatype-lexical. |
Elements without a class #
The read-only renderer leaves some elements bare. Reach them by structure, which ties the stylesheet to the markup.
| Selector | Element | What it styles |
|---|---|---|
.prose-policy > h3 | h3 | The policy heading. |
.prose-policy > p | p | The intro sentence. |
.prose-rule > ul | ul | The conditions and refinements of a rule, and the rules nested in a follow-up list. They follow a p.prose-label, which is the only thing telling them apart. |
.prose-glossary dt, .prose-glossary dd | dt, dd | A glossary term (a span with the label, then a code with the written form) and its plain definition. |
The editor view, OdrlProseView, gives conditions and follow-up lists classes. The read-only renderer does not. This is a gap in the library, not a rule.
Custom properties #
The read-only renderer reads no custom properties, and the Angular package sets none. Theming with variables works because custom properties inherit: define them on ds-odrl-prose or an ancestor, and read them with var() in the rules that style the classes above. The example does exactly that with --ds-* tokens.
Example stylesheet #
prose.css is a complete starting point: the policy, the glossary and the validation findings, in light and in dark. It defines its own tokens in light, in dark by system preference and in dark by data-theme="dark" on <html>, and it sets ds-odrl-prose { display: block; }. Rename the tokens to yours and keep the selectors. The same file is served at examples/prose.css, and the home page of this site loads it. A test renders the real output with it in Chrome and checks the colours.
Register it as a global stylesheet; see Styling for why a component's own styles do not reach the output.
/* ds-prose-rs: example stylesheet for the rendered policy (light and dark).
The library ships no CSS for the read-only renderer. This file is a complete
starting point: copy it, rename the --ds-* tokens to yours, keep the selectors.
Editor overrides live in prose-editor.css and read the tokens defined here. */
/* Tokens: light first, then dark by system preference, then an explicit
data-theme on <html> that wins over the system in both directions. */
:root {
color-scheme: light dark;
--ds-fg: #1f2328;
--ds-bg: #ffffff;
--ds-muted: #57606a;
--ds-border: #d0d7de;
--ds-accent: #0969da;
--ds-allow: #15803d;
--ds-deny: #b91c1c;
--ds-duty: #1d4ed8;
--ds-warn: #b45309;
--ds-focus: #0969da;
--ds-surface: #f6f8fa;
--ds-selected-bg: #0969da;
--ds-selected-fg: #ffffff;
/* The two custom properties the library reads (editor only). */
--prose-error: var(--ds-deny);
--prose-warning: var(--ds-warn);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--ds-fg: #e6edf3;
--ds-bg: #0d1117;
--ds-muted: #9198a1;
--ds-border: #3d444d;
--ds-accent: #4493f8;
--ds-allow: #3fb950;
--ds-deny: #f85149;
--ds-duty: #58a6ff;
--ds-warn: #d29922;
--ds-focus: #4493f8;
--ds-surface: #151b23;
--ds-selected-bg: #1f6feb;
--ds-selected-fg: #ffffff;
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--ds-fg: #e6edf3;
--ds-bg: #0d1117;
--ds-muted: #9198a1;
--ds-border: #3d444d;
--ds-accent: #4493f8;
--ds-allow: #3fb950;
--ds-deny: #f85149;
--ds-duty: #58a6ff;
--ds-warn: #d29922;
--ds-focus: #4493f8;
--ds-surface: #151b23;
--ds-selected-bg: #1f6feb;
--ds-selected-fg: #ffffff;
}
:root[data-theme="light"] {
color-scheme: light;
}
/* Angular only: an unknown element is display: inline. */
ds-odrl-prose {
display: block;
}
/* The policy */
.ds-prose {
color: var(--ds-fg);
line-height: 1.5;
}
.ds-prose .prose-policy {
margin: 0 0 2rem;
}
.ds-prose .prose-policy > h3,
.ds-prose .prose-heading {
margin: 0 0 0.25rem;
font-size: 1.1rem;
}
.ds-prose .prose-rules {
padding-left: 1.25rem;
}
.ds-prose .prose-rule {
margin: 0 0 1rem;
}
.ds-prose .prose-rule-sentence {
margin: 0 0 0.25rem;
padding-left: 0.5rem;
font-weight: 600;
border-left: 4px solid var(--ds-border);
}
.ds-prose .prose-permission > .prose-rule-sentence {
border-left-color: var(--ds-allow);
}
.ds-prose .prose-prohibition > .prose-rule-sentence {
border-left-color: var(--ds-deny);
}
.ds-prose .prose-obligation > .prose-rule-sentence {
border-left-color: var(--ds-duty);
}
.ds-prose .prose-label {
margin: 0.4rem 0 0.1rem;
font-size: 0.9rem;
color: var(--ds-muted);
}
.ds-prose .prose-notes {
font-size: 0.85rem;
color: var(--ds-muted);
}
.ds-prose .prose-term {
text-decoration: underline dotted;
text-underline-offset: 2px;
}
.ds-prose .prose-inherited,
.ds-prose .prose-empty {
color: var(--ds-muted);
font-style: italic;
}
.ds-prose .prose-warnings {
font-size: 0.85rem;
padding: 0.4rem 0.8rem;
border: 1px solid var(--ds-warn);
border-radius: 6px;
}
.ds-prose .prose-error,
ds-odrl-prose > .prose-error {
color: var(--ds-deny);
}
/* Glossary of profile terms */
.ds-prose .prose-glossary {
margin-top: 0.75rem;
font-size: 0.9rem;
}
.ds-prose .prose-glossary dt {
margin-top: 0.4rem;
font-weight: 600;
}
.ds-prose .prose-glossary dt code {
font-weight: 400;
color: var(--ds-muted);
}
.ds-prose .prose-glossary dd {
margin-left: 1rem;
}
.ds-prose .prose-note,
.ds-prose .prose-kind-of,
.ds-prose .prose-implies,
.ds-prose .prose-from {
color: var(--ds-muted);
}
.ds-prose .prose-deprecated {
color: var(--ds-warn);
}
/* Validation findings and the verdict */
.ds-prose .prose-violations-status {
display: inline-block;
margin: 0 0 0.5rem;
padding: 0.3rem 0.7rem;
border: 1px solid currentColor;
border-radius: 6px;
font-weight: 600;
}
.ds-prose .prose-conforms {
color: var(--ds-allow);
}
.ds-prose .prose-incomplete {
color: var(--ds-warn);
}
.ds-prose .prose-violations-found {
color: var(--ds-deny);
}
.ds-prose .prose-violations-list {
font-size: 0.9rem;
}
.ds-prose .prose-violation-error {
color: var(--ds-deny);
}
.ds-prose .prose-violation-warning {
color: var(--ds-warn);
}
.ds-prose .prose-violation-info {
color: var(--ds-muted);
}
.ds-prose .prose-rule-id {
color: var(--ds-muted);
}