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/.

SelectorElementWhat it styles
.ds-prosearticleThe root. The class prop, or articleClass in Angular, is appended here.
.prose-policysectionOne policy: an h3, an intro p, then the parts below.
.prose-notesulNotes about the policy, only when there are some.
.prose-rulesolThe rules of one policy.
.prose-ruleliOne rule. A duty, remedy or consequence nested under a rule is also a li.prose-rule.
.prose-permission, .prose-prohibition, .prose-obligationliThe kind of the rule, next to .prose-rule. A duty is a .prose-obligation.
.prose-rule-sentencepThe sentence of a rule.
.prose-referenceaThe 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-irispan, aA 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-labelpA lead-in such as "Applies only if:", and the heading of the warnings block.
.prose-warningsasideThe "Not rendered" block: parts of the policy the renderer did not turn into sentences.
.prose-errorp[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-glossarydetails[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-fromddOne line of a glossary entry: usage note, "A kind of", "Implies", deprecation, source profile.
.prose-violationssectionValidation findings and the verdict, after the policies. Written only with a validator and the findings switched on.
.prose-violations-statusp[role="status"]The verdict line. Written only for a strict validation.
.prose-conforms, .prose-incomplete, .prose-violations-foundpThe verdict, next to .prose-violations-status: conforms, cannot be certified, does not conform.
.prose-violations-listulThe findings.
.prose-violationliOne finding.
.prose-violation-error, .prose-violation-warning, .prose-violation-infoliThe severity of a finding, next to .prose-violation.
.prose-rule-idcodeThe 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.

SelectorElementWhat it styles
.prose-policy > h3h3The policy heading.
.prose-policy > ppThe intro sentence.
.prose-rule > ululThe 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 dddt, ddA 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);
}