Changelog
Source:NEWS.md
omni 1.1.0
ggplot color scales
-
scale_fill_omni_discrete()andscale_color_omni_discrete()now use the first k palette colors for k categories instead of interpolating k colors across the five-color ramp. Categorical charts get the exact brand colors at every count (three categories are periwinkle, orange-red and plum; four add olive-green), and the scale errors clearly when more categories than palette colors are requested (#242).
omni_span()
- Fixed the colour never applying.
omni_span()built a raw-hex marquee tag by prefixing"{#"and then interpolatingomni_colors(), which already returns a leading#- so it emitted{##5776B2 Housing}, which marquee cannot parse. The phrase rendered in the base colour with no error and no warning. It now emits the class-based tag,{.periwinkle-600 Housing}, which is whatomni_header()’s own documentation already described and whatkeywordandfinding_keywordhave always used internally - one resolution path for every coloured phrase in the header instead of two. -
omni_span()now errors on a colour name that is not a brand colour. Previously an unknown name produced an unregistered tag, which fails the same silent way the malformed hex did.
Chart defaults
-
Breaking-ish: data marks now default to a 600-level brand colour (
periwinkle-600) instead of the chart gray.set_omni_defaults()was setting every bar, column, point, line, boxplot, density and violin - and the matching stat defaults - tochart-gray, so any chart that did not call out a single group came out entirely gray. The guidance is that the chart gray is for de-emphasising the other groups when one group is highlighted, not for the whole chart. -
set_omni_defaults()’sbase_colorargument is renamedprimary_color, which better describes what it does, and now accepts a brand colour name ("orange-red-600") as well as a hex.base_colorstill works and warns. Same forset_client_defaults(), whose client blue is unchanged. - Documented that switching the colour part-way through a report by calling
set_omni_defaults()again is unsafe:update_geom_defaults()resolves when a plot is drawn, not when it is built, so a second call repaints every plot object that has not been printed yet - a report that builds figures into a list or saves them at the end would silently get the last colour for all of them. To vary the colour per figure, set it on the geom (geom_col(fill = omni_colors("orange-red-600"))), which is captured when the layer is created.
omni_header()
Fixed
finding_keyword(the secondary-finding stripe/text in the caption) silently rendering gray instead ofcolor. It colors via marquee markdown ({.color ...}), which only resolves ifplot.caption’s style has that color registered as a tag -omni_header()was never supplying that style itself, so it only worked by accident, whentheme_omni()’s ownplot.captionelement (which does carry the right style) was still in place immediately beforehand for ggplot2’s theme-merge to inherit it from. Any code resettingplot.captionfirst - including a fix for an unrelated title/subtitle theme-merge issue - broke it.omni_header()now supplies its own style unconditionally, independent of theme order.Fixed the header elements feeling scrunched together: added space above the eyebrow (
top_header), which previously sat flush against the plot’s outer margin, and more room below the measure description (measure) before the panel starts. The gap between the eyebrow and the primary finding itself is unchanged - the markdown renderer behind that text box only offers that specific gap in one fixed size, and design feedback was that size reads as too much space.Fixed the caption (secondary finding and source/N) rendering larger and bold, heavier than the measure description above it - the reverse of the intended hierarchy. It now renders at the smaller, normal-weight, italic styling
theme_omni()has always specified for captions. This was a regression from thefinding_keywordcolor fix above: passing the shared base style explicitly fixed the color but silently brought the base’s own larger, bold typography with it.theme_omni()andomni_header()now buildplot.caption’s style from one shared helper, so the two can’t drift apart again.Captions are now left-aligned under
theme_omni()as well as underomni_header().theme_omni()styled the caption’s size, weight and italic but never its alignment, leaving ggplot2’s right-aligned default - so a chart built withtheme_omni()alone put source/N bottom-right, while the same chart built throughomni_header()put it bottom-left. Left is the brand standard. Note this changes existing figures that usetheme_omni()with a caption but withoutomni_header(): their caption moves from the bottom-right to the bottom-left when re-rendered.Fixed the primary finding rendering in
theme_omni()’s title gray (#666665) instead of navy.omni_header()setplot.title’s style but not itscolour, andelement_marquee()’s owncolouroverrides the style’s base color - so withcolourleft unset it inherited whateverplot.titlethe active theme already had. The colored keyword was unaffected (it is a tag, which overrides the base), which is part of why this survived review. Third instance of one shape: an element built without being fully specified inheriting stale state fromtheme_omni().The measure description (
plot.subtitle) is now a marquee element rather than a plainelement_text(). It was the last slot whereomni_header()andtheme_omni()disagreed on class - the shape of mismatch that caused theme-merge trouble on the title and caption before those were converted. It also means the measure description wraps to the plot width: a long one previously ran off the right edge and clipped, so any script wrapping it by hand can stop. Spacing and colour are unchanged - the margins were re-tuned against rendered output because marquee brings its own line-box leading, which shifted the gaps by 6 and 18 pixels at the old margin.Tightened the header’s vertical rhythm against design feedback that the elements sat too far apart: the gap between the primary finding and the measure description is down ~60% and the gap between the measure description and the plot panel is down ~10%. Both were set by measuring the rendered output rather than by picking margin values, since the visible gap is the margin plus the font’s own line box.
The gap between the eyebrow (
top_header) and the primary finding is now adjustable, via a neweyebrow_gapargument (default0, the tightest the two lines go - the residual gap there is the fonts’ line boxes, which no margin can shrink). Previously the header’s five elements read as scrunched together, but this particular gap could not be opened up: the title was rendered by ggtext, whose renderer only offers that gap in one fixed step - too large per design feedback - with no font-size or line-height trick able to shrink it. The title is now rendered by marquee, which exposes the gap as a real block margin while still wrapping long findings to the plot width (the reason ggtext’s textbox was chosen originally).eyebrow_gaponly moves the eyebrow; the space below the primary finding is unchanged.Breaking: because the title is now marquee markdown rather than HTML,
omni_span()returns marquee markdown instead of an HTML<span>. Existingomni_span()calls insideprimarykeep working. Butomni_span()no longer works for coloring axis labels - axis text is rendered by ggtext, which reads HTML, not marquee markdown. Useomni_highlight_labels()for axis labels, which is what it is for. Note that an HTML span passed intoprimarynow renders as uncolored text rather than erroring, so any hand-written<span>in a header needs converting.The secondary finding (
plot.caption) now wraps to the plot width, as the title and measure description already did. It was the one marquee slot left withoutwidth, so a finding longer than the canvas was clipped mid-word instead of breaking - silently.theme_omni()was missing the same field on both its subtitle and its caption; all three slots in both functions now set it. Scripts wrappingfindingby hand can stop, which also removes a failure mode of its own:finding_keywordis matched as a fixed string, so a hand-inserted line break landing inside the keyword phrase stopped it matching and dropped the stripe and color.Documented that
omni_header()clears both axis titles. It setslabs(x = NULL, y = NULL)- the brand standard drops axis titles and the measure description carries what is being measured - so alabs()call placed before the header was discarded with no error and no warning. The help page now says so and shows the working order (labs()after the header), for the charts that do need one, such as a count axis on a histogram. Behaviour is unchanged; this was previously recorded only in a source comment.Breaking: fixed all three header text slots rendering 15-27% too small.
marquee::element_marquee()carries its ownsize, and it takes precedence over the marquee style’sbasesize. Bothomni_header()andtheme_omni()set the intended size only inside the style, so it never applied and every slot fell back to whatever the base theme supplied -theme_minimal(11)’srel()defaults of 13.2 / 11 / 8.8pt against the brand’s 18 / 13 / 11pt. The title was rendering below the brand’s 14pt minimum for a primary header.primary_sizehad no effect at all. It reached the style but not the element, so the rendered title measured identically at 12, 18, 24 and 30. It now works.eyebrow_sizewas always fine: the eyebrow is anh1class in the style, and class-level sizes do override the element - onlybaseloses. That asymmetry is why the problem survived review, since the one size argument anyone tested by eye was the one that worked.Expect existing figures to change. Header text grows on every chart, and the caption’s 8.8 to 11pt jump is large enough to reflow wrapping. This is a correction toward the brand standard rather than a preference, but it is visible and will shift layouts.
eyebrow_sizenow defaults to 11pt rather than 10. Not a bug fix;eyebrow_sizealways worked. The eyebrow was the only element sitting below the brand’s 11pt floor for figure text, and it is the one element that can least afford it: it is the only ALL CAPS text on a chart, and uppercase removes the ascender and descender cues readers use to recognise word shapes, so it needs more size than mixed case for equal legibility. Measuring the training template agrees - anchoring the source note at 11pt puts its eyebrow near 11.8pt. This is a legibility and hierarchy judgement, not a contrast fix: at both 10 and 11pt bold the eyebrow is below the WCAG large-text cutoff, so chart-gray is held to 4.5:1 either way, and it passes at 4.54:1 before and after.
omni_highlight_labels()
-
Breaking:
coloris now required instead of defaulting to"orange-red-600". A chart uses one highlight color and the colored axis label has to match the bar or point it labels, but the default silently produced an orange-red label on charts highlighted in any other color - a brand violation with nothing in the rendered output to flag it, and invisible to anyone using a tool that writes the call for them. Omittingcolornow raises an error naming the fix. Update existing calls by passing the same color given toomni_header(); calls that were relying on the default and are genuinely orange-red charts needcolor = "orange-red-600"added.
PDF report tables
- Table columns no longer change width where a table breaks across pages. Tables now keep consistent, content-proportional column widths on every page (#238).
- Table header rows now repeat at the top of each page a table spans (#238).
- Tables no longer draw white horizontal lines between rows; rows are separated by the zebra shading and the vertical column dividers are kept. Applies to HTML, Word and PDF output (#240).
-
omni_table()gained a fuller help page, including how to set column widths manually withflextable::width()andflextable::set_table_properties(layout = "fixed")(#238).
PDF report layout
- Fixed the spacing between paragraphs so paragraphs are clearly separated. Previously the gap between paragraphs matched the line spacing within a paragraph, so paragraphs ran together; manual blank lines are no longer needed to separate them (#237).
- Fixed spacing above and below section headings (
##/###/####) so it matches the Word brand template (Report Template.dotx). The primary heading level (##) previously had almost no space below it before body text, and the two lower levels had spacing that didn’t match Word either. - Corrected the heading spacing values from the fix above. They were measured from the Word template’s built-in “Heading 1/2/3” styles, which the template doesn’t actually use (they render in an unbranded orange, not Omni’s navy) - the real reference is the template’s custom “Omni Header 1/2/3” styles, which give uniform spacing across all three levels rather than the hierarchical spacing the built-in styles have. All three levels (
##/###/####) now get the same ~11pt before / ~12pt after spacing. - Added a manual fix for a heading that lands at the top of a printed page: the normal spacing above a heading looks like too much space there since there’s nothing above it on that page to separate from, and the renderer has no way to detect that position automatically at CSS-authoring time. After knitting, add the
remove-header-spaceclass to a heading that visibly lands at the top of a page and re-knit. - Corrected how to apply the
remove-header-spaceclass from the fix above. It must be added as a header attribute -## My Heading {.remove-header-space}- not by wrapping the heading in a fenced div (::: {.remove-header-space}/ heading /:::), which was the originally documented form. The fenced-div form forces the heading’s own section to close immediately after it, orphaning any subsections - and their table-of-contents entries - that should nest inside it as flat siblings instead, breaking the table of contents for the rest of that section.
HTML report
- The report footer now shows the year the report is knitted rather than a hard-coded year, so it no longer needs a manual update each January (#243).
- Fixed spacing above and below section headings (h1-h4) so it matches the Word brand template (Report Template.dotx). Previously every heading level used the same spacing, which didn’t match Word’s per-level spacing and, for h1, was visibly tighter than the gap under a top-level heading in Word (#257).
- Fixed spacing between paragraphs and around bulleted/numbered lists, which was noticeably tighter than the Word brand template. Paragraph-to-paragraph and list spacing now match the gaps in Word.
- Corrected which heading level receives which spacing from the fix above. The report-html skeleton starts sections at
##, not#, so h2/h3/h4 are the levels actually used for primary/subtopic/sub-subtopic headings in practice; the previous fix had applied each level’s spacing one tag too high. - Corrected the heading spacing values themselves, for the same reason as the PDF report fix above: they were measured from the Word template’s unbranded built-in “Heading 1/2/3” styles rather than its custom “Omni Header 1/2/3” styles. All heading levels now get the same ~11pt before / ~12pt after spacing, matching Omni Header 1/2/3’s uniform spacing.