diff --git a/ui/all-translations-het-archief.json b/ui/all-translations-het-archief.json index a5b3ba300..d7480ed58 100644 --- a/ui/all-translations-het-archief.json +++ b/ui/all-translations-het-archief.json @@ -1724,9 +1724,9 @@ "app": "HET_ARCHIEF", "component": "ADMIN_CORE", "location": "modules/content-page/const/content-block", - "key": "poederblauw", + "key": "babyblauw", "language": "nl", - "value": "Poederblauw", + "value": "Baby blauw", "value_type": "TEXT" }, { @@ -1734,9 +1734,9 @@ "app": "HET_ARCHIEF", "component": "ADMIN_CORE", "location": "modules/content-page/const/content-block", - "key": "poederblauw", + "key": "babyblauw", "language": "en", - "value": "Poederblauw", + "value": "Baby blauw", "value_type": "TEXT" }, { @@ -58539,4 +58539,4 @@ "value": "You do not have the right permissions to call this route", "value_type": "TEXT" } -] \ No newline at end of file +] diff --git a/ui/all-translations-het-archief.sql b/ui/all-translations-het-archief.sql index 427648aa4..d5baef258 100644 --- a/ui/all-translations-het-archief.sql +++ b/ui/all-translations-het-archief.sql @@ -1563,6 +1563,8 @@ INSERT INTO app.translations ("component", "location", "key", "value", "value_ty INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/components/content-page-renderer/content-page-renderer', 'bewerk-pagina-tooltip', 'Edit page tooltip', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Edit page tooltip', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/components/date-picker/date-picker', 'datum-input-aria-label', 'Kies hier de datum', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Kies hier de datum', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/components/date-picker/date-picker', 'datum-input-aria-label', 'Specify the date here', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Specify the date here', value_type = 'TEXT'; +INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'babyblauw', 'Baby blauw', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Baby blauw', value_type = 'TEXT'; +INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'babyblauw', 'Baby blauw', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Baby blauw', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'bloesem-roze', 'Bloesem roze', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Bloesem roze', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'bloesem-roze', 'Bloesem roze', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Bloesem roze', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'honing-geel', 'Honing geel', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Honing geel', value_type = 'TEXT'; @@ -1583,8 +1585,6 @@ INSERT INTO app.translations ("component", "location", "key", "value", "value_ty INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'pistache-groen', 'Pistache groen', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Pistache groen', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'platinum', 'Platinum', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Platinum', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'platinum', 'Platinum', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Platinum', value_type = 'TEXT'; -INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'poederblauw', 'Poederblauw', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Poederblauw', value_type = 'TEXT'; -INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'poederblauw', 'Poederblauw', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Poederblauw', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'salie-groen', 'Salie groen', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Salie groen', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'salie-groen', 'Salie groen', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Salie groen', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('ADMIN_CORE', 'modules/content-page/const/content-block', 'sky-blauw', 'Sky blauw', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Sky blauw', value_type = 'TEXT'; @@ -5860,4 +5860,4 @@ INSERT INTO app.translations ("component", "location", "key", "value", "value_ty INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('FRONTEND', 'pages/zoeken/index', 'zoek-pagina-seo-omschrijving', 'Je kan op deze website centraal en online zoeken doorheen beschrijvingen van materiaal van meer dan 140 aanbieders, zonder dat je je hoeft te verplaatsen.', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Je kan op deze website centraal en online zoeken doorheen beschrijvingen van materiaal van meer dan 140 aanbieders, zonder dat je je hoeft te verplaatsen.', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('FRONTEND', 'pages/zoeken/index', 'zoek-pagina-seo-omschrijving', 'You can search centrally and online throughout descriptions of materials from more than 140 organisations without having to move.', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'You can search centrally and online throughout descriptions of materials from more than 140 organisations without having to move.', value_type = 'TEXT'; INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('FRONTEND', 'pages/zoeken/index', 'zoeken-pagina-titel', 'Search', 'TEXT', 'en') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Search', value_type = 'TEXT'; -INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('FRONTEND', 'pages/zoeken/index', 'zoeken-pagina-titel', 'Zoeken', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Zoeken', value_type = 'TEXT'; \ No newline at end of file +INSERT INTO app.translations ("component", "location", "key", "value", "value_type", "language") VALUES ('FRONTEND', 'pages/zoeken/index', 'zoeken-pagina-titel', 'Zoeken', 'TEXT', 'nl') ON CONFLICT (component, location, key, language) DO UPDATE SET value = 'Zoeken', value_type = 'TEXT'; diff --git a/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.scss b/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.scss index 4c64bae06..36fef7517 100644 --- a/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.scss +++ b/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.scss @@ -1,4 +1,5 @@ @use "../../../shared/styles/settings/colors" as colors; +@use "../../../shared/styles/utilities/background-text" as background-text; .c-content-page-overview-block__header { opacity: 0; @@ -62,3 +63,19 @@ } } } + +/* -------------------------------------------------------------------------- + WCAG text colors on a content block's background color + + ContentBlockRenderer sets --bg-text-primary / --bg-text-secondary / --bg-text-hyperlink from + the design record. The wrapper applies primary text by inheritance; elements that have their own + color rule are tagged explicitly with one of the role classes below. Rich-text containers use + u-background-text-links because their generated anchors cannot receive a React class directly. + https://meemoo.atlassian.net/browse/ARC-3848 + + ContentBlockRenderer emits the shared mixin in client.css; the utility index emits it separately + in admin.css. All role rules are scoped to u-background-text-colors, so AVO remains untouched. + Secondary and link roles fall back to primary when the design specifies no separate role color. + -------------------------------------------------------------------------- */ + +@include background-text.background-text-roles; diff --git a/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.tsx b/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.tsx index ebe4043ba..8cbeb35e5 100644 --- a/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.tsx +++ b/ui/src/react-admin/modules/content-page/components/ContentBlockRenderer/ContentBlockRenderer.tsx @@ -2,7 +2,7 @@ import { Container, Spacer } from '@viaa/avo2-components'; import clsx from 'clsx'; import { kebabCase, noop, omit } from 'es-toolkit'; -import type { FunctionComponent, KeyboardEvent, RefObject } from 'react'; +import type { CSSProperties, FunctionComponent, KeyboardEvent, RefObject } from 'react'; import React, { useCallback, useEffect, useRef } from 'react'; import { AdminConfigManager } from '~core/config/config.class'; import { getCommonUser } from '~core/config/config.selectors.ts'; @@ -10,7 +10,8 @@ import { GENERATED_CONTENT_BLOCK_ANCHOR_PREFIX } from '~modules/content-page/con import type { ContentPageInfo } from '~modules/content-page/types/content-pages.types'; import { ContentPageWidth } from '~modules/content-page/types/content-pages.types'; import { generateSmartLink } from '~shared/components/SmartLink/SmartLink'; -import { GET_DARK_BACKGROUND_COLOR_OPTIONS } from '../../const/get-color-options'; +import { getBackgroundTextColorVariables } from '../../const/background-text-colors'; +import { hasDarkBackground } from '../../const/get-color-options'; import { Color, type ContentBlockConfig, @@ -122,9 +123,15 @@ const ContentBlockRenderer: FunctionComponent = ({ }; } - const hasDarkBg = GET_DARK_BACKGROUND_COLOR_OPTIONS().includes( - blockState?.backgroundColor || ('' as unknown as Color) - ); + const hasDarkBg = hasDarkBackground(blockState?.backgroundColor); + // The Archief text colors specified for this background, published as css variables so text + // inside the block can take the primary, secondary or hyperlink role. On AVO this helper returns + // no variables, preserving AVO's own brand-book behavior. + // https://meemoo.atlassian.net/browse/ARC-3848 + const textColorVariables = getBackgroundTextColorVariables( + blockState?.backgroundColor + ) as CSSProperties; + const hasBackgroundTextColors = Object.keys(textColorVariables).length > 0; const anchor = blockState?.anchor?.replaceAll(' ', '-') || GENERATED_CONTENT_BLOCK_ANCHOR_PREFIX + contentBlockConfig.id; @@ -146,6 +153,7 @@ const ContentBlockRenderer: FunctionComponent = ({ ? Color.Transparent : blockState?.backgroundColor, ...(blockState?.headerBackgroundColor !== Color.Transparent ? { zIndex: 1 } : {}), + ...textColorVariables, }} data-anchor={anchor} ref={blockRef} @@ -161,11 +169,13 @@ const ContentBlockRenderer: FunctionComponent = ({ * to avoid overlapping a fixed header when we jump to this anchor * https://meemoo.atlassian.net/browse/AVO-3351 */} -
+
= ({ // biome-ignore lint/suspicious/noExplicitAny: todo return (labelObj as any).link_to ? (
); diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockHetArchiefImageTextBackground/BlockHetArchiefImageTextBackground.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockHetArchiefImageTextBackground/BlockHetArchiefImageTextBackground.tsx index 0adc663a1..d7b565969 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockHetArchiefImageTextBackground/BlockHetArchiefImageTextBackground.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockHetArchiefImageTextBackground/BlockHetArchiefImageTextBackground.tsx @@ -99,7 +99,6 @@ export const BlockHetArchiefImageTextBackground: FunctionComponent< // Only reads it once the image has finished loading — reading mid-load measures // ~0px, which would shrink `.media` (and the image with it) with no way to // recover, since a later re-measure would just report that same self-inflicted size. - // biome-ignore lint/correctness/useExhaustiveDependencies: refs are stable const updateMediaMeasurements = useCallback(() => { const imgEl = imgRef.current; const measureEl = copyrightMeasureRef.current; @@ -172,6 +171,7 @@ export const BlockHetArchiefImageTextBackground: FunctionComponent< {heading} @@ -206,12 +206,7 @@ export const BlockHetArchiefImageTextBackground: FunctionComponent< > {image && (
- {imageAltText} + {imageAltText}
)}
= ({ highlightColor === CustomBackground.MeemooLogo ? Color.Transparent : ((ColorSelectGradientColors as Record)[highlightColor] ?? highlightColor); + // Text colors follow the actual fill behind the content: gradients use the white content box, + // while the transparent meemoo logo variant keeps the outer block's inherited text color. + // https://meemoo.atlassian.net/browse/ARC-3848 + const textBoxBackground = isGradient + ? Color.White + : highlightColor === CustomBackground.MeemooLogo + ? Color.Transparent + : highlightColor; + const textColorVariables = getBackgroundTextColorVariables(textBoxBackground); + const hasTextColors = Object.keys(textColorVariables).length > 0; return (
= ({ />
+ />
= {title} - +
cleanup()); + +const ELEMENT = { + source: '/image.jpg', + title: 'Grid title', + text: 'Grid description', +}; + +describe(' text colors', () => { + it('uses background text roles when no foreground color was supplied', () => { + const { container } = render(); + + expect(container.querySelector('.c-block-grid__text-wrapper')).toHaveClass( + 'u-background-text-primary' + ); + expect(screen.getByText('Grid title')).not.toHaveClass('u-background-text-primary'); + expect(screen.getByText('Grid description')).not.toHaveClass('u-background-text-primary'); + }); + + it('preserves a caller-supplied foreground color', () => { + const { container } = render(); + const textWrapper = container.querySelector('.c-block-grid__text-wrapper'); + + expect(textWrapper).not.toHaveClass('u-background-text-primary'); + expect(textWrapper).toHaveStyle({ color: '#123456' }); + expect(screen.getByText('Grid title')).not.toHaveClass('u-background-text-primary'); + }); +}); diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageGrid/BlockImageGrid.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockImageGrid/BlockImageGrid.tsx index 4d917ef6f..ebf2a9b3e 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageGrid/BlockImageGrid.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockImageGrid/BlockImageGrid.tsx @@ -24,7 +24,7 @@ export const BlockImageGrid: FunctionComponent = ({ textSize = 15, textMargin = 0, textWeight = 500, - textColor = '#2B414F', + textColor, horizontalMargin = 10, verticalMargin = 10, className, @@ -36,7 +36,7 @@ export const BlockImageGrid: FunctionComponent = ({ {element.textAbove && (
-

{element.textAbove}

+

{element.textAbove}

)} @@ -64,10 +64,10 @@ export const BlockImageGrid: FunctionComponent = ({ showIcon={element.copyrightIconVisible} />
{!!element.title && ( diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.scss b/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.scss deleted file mode 100644 index 93753e058..000000000 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.scss +++ /dev/null @@ -1,7 +0,0 @@ -@use "../../../../shared/styles/settings/colors" as colors; - -.c-block-image-title-text-button { - .a-subtitle { - color: colors.$color-gray-400; - } -} diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.test.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.test.tsx index 318982e10..138c0ea07 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.test.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.test.tsx @@ -15,6 +15,7 @@ const blockImageTitleTextButtonExample = ( imageSource="https://placeholder.com/1280x720.jpg" imageDescription="image showing the default dimensions on a grey background" title="Title" + subtitle="Subtitle" text={loremIpsumText} buttonLabel="Goto video" /> @@ -43,9 +44,14 @@ describe('', () => { }); it('Should render the text correctly', () => { - render(blockImageTitleTextButtonExample); + const { container } = render(blockImageTitleTextButtonExample); const pElement = screen.getByText(loremIpsumText); expect(pElement).toBeInTheDocument(); + expect(screen.getByRole('heading', { level: 2 })).toHaveClass('u-background-text-primary'); + expect(container.querySelector('.u-background-text-secondary')).toHaveTextContent('Subtitle'); + expect( + container.querySelector('.u-background-text-primary.u-background-text-links') + ).toHaveTextContent(loremIpsumText); }); it('Should set the correct className', () => { diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.tsx index a87692da0..415319162 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockImageTitleTextButton/BlockImageTitleTextButton.tsx @@ -13,8 +13,6 @@ import type { FunctionComponent, ReactNode } from 'react'; import Html from '~shared/components/Html/Html'; import { SanitizePreset } from '~shared/helpers/sanitize/presets'; -import './BlockImageTitleTextButton.scss'; - export interface BlockImageTitleTextButtonProps extends DefaultProps { imageSource: string; imageDescription?: string; @@ -42,8 +40,8 @@ export const BlockImageTitleTextButton: FunctionComponent + className={clsx(className, 'u-background-text-links')} + /> ); } return text; @@ -60,9 +58,9 @@ export const BlockImageTitleTextButton: FunctionComponent
- {title &&

{title}

} - {renderText(subtitle, 'a-subtitle')} - {renderText(text)} + {title &&

{title}

} + {renderText(subtitle, 'u-background-text-secondary')} + {renderText(text, 'u-background-text-primary')} {buttonLabel && (
{buttonLabel && (
- {buttonLabel} + + {buttonLabel} +
)}
@@ -60,11 +62,7 @@ export const BlockMaintainersGrid: FunctionComponent = } )} > - + ); diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewNewspaperTitles/BlockOverviewNewspaperTitles.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewNewspaperTitles/BlockOverviewNewspaperTitles.tsx index 8e176a0ed..d3dfc41fe 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewNewspaperTitles/BlockOverviewNewspaperTitles.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockOverviewNewspaperTitles/BlockOverviewNewspaperTitles.tsx @@ -86,6 +86,7 @@ export const BlockOverviewNewspaperTitles: FC key={`newspaper-title-item__${item.title}`} > = ({ group, groupIndex, themes, bandColor }) => { const gridRef = useRef(null); const [bandHeight, setBandHeight] = useState(null); + // The group title sits on the band, so it takes the design text colors for the band color. + // https://meemoo.atlassian.net/browse/ARC-3848 + const bandTextColorVariables = getBackgroundTextColorVariables(bandColor); useLayoutEffect(() => { const gridEl = gridRef.current; @@ -121,7 +125,10 @@ export const BlockOverviewThemesGroupSection: FunctionComponent< }; return ( -
+
{!!bandHeight && ( <>
= ({ const getDescription = (page: ContentPageInfo) => { return showDescription && page.description ? ( - + ) : undefined; }; @@ -191,7 +196,7 @@ export const BlockPageOverview: FunctionComponent = ({ ); } @@ -286,15 +291,19 @@ export const BlockPageOverview: FunctionComponent = ({ value: page.path, } as ButtonAction, itemStyle === ContentItemStyle.NEWS_LIST ? ( -

{page.title}

+

{page.title}

) : ( -

{page.title}

+

{page.title}

), page.title )} - {showDate && renderText(formatDateString(dateString, page), 'a-subtitle')} + {showDate && + renderText( + formatDateString(dateString, page), + 'a-subtitle u-background-text-secondary' + )} { -
+
{renderText(getDescription(page))}
} @@ -356,17 +365,17 @@ export const BlockPageOverview: FunctionComponent = ({ ); }); - } else { - // Render all pages in a grid without section titles (unique pages only) - let pagesToShow = labelsToShow.flatMap((labelObj) => { - if (!(pagesByLabel[labelObj.id] || []).length) { - return []; - } - return pagesByLabel[labelObj.id]; - }); - pagesToShow = uniqBy(pagesToShow, (page) => page.id); - return renderGrid(pagesToShow); } + + // Render all pages in a grid without section titles (unique pages only) + let pagesToShow = labelsToShow.flatMap((labelObj) => { + if (!(pagesByLabel[labelObj.id] || []).length) { + return []; + } + return pagesByLabel[labelObj.id]; + }); + pagesToShow = uniqBy(pagesToShow, (page) => page.id); + return renderGrid(pagesToShow); } if (itemStyle === ContentItemStyle.ACCORDION) { // Ensure the focused page is not loaded twice on the same pagination page (ACCORDION) diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockPageOverview/BlockPageOverview.wrapper.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockPageOverview/BlockPageOverview.wrapper.tsx index 1702b1467..b8b3c4ae9 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockPageOverview/BlockPageOverview.wrapper.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockPageOverview/BlockPageOverview.wrapper.tsx @@ -7,7 +7,7 @@ import { ContentItemStyle } from '~content-blocks/BlockPageOverview/BlockPageOve import { AdminConfigManager } from '~core/config/config.class'; import { BlockPageOverview } from '~modules/content-page/components/blocks/BlockPageOverview/BlockPageOverview'; import type { PageOverviewWrapperProps } from '~modules/content-page/components/blocks/BlockPageOverview/BlockPageOverview.types'; -import { GET_DARK_BACKGROUND_COLOR_OPTIONS } from '~modules/content-page/const/get-color-options'; +import { hasDarkBackground } from '~modules/content-page/const/get-color-options'; import { useGetContentPageByLanguageAndPath } from '~modules/content-page/hooks/use-get-content-page-by-language-and-path'; import { useGetContentPageLabelsByTypeAndIds } from '~modules/content-page/hooks/use-get-content-page-labels-by-type-and-ids'; import { useGetContentPageLabelsByTypeAndLabels } from '~modules/content-page/hooks/use-get-content-page-labels-by-type-and-labels'; @@ -223,10 +223,7 @@ export const BlockPageOverviewWrapper: FunctionComponent', () => { const contentContainer = container.querySelector('.c-rich-text-editor__content'); expect(container.firstChild).toHaveClass(customClass); expect(contentContainer).not.toBeNull(); + expect(contentContainer).toHaveClass('u-background-text-links'); }); it('Should create multiple columns', () => { diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockRichText/BlockRichText.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockRichText/BlockRichText.tsx index 0d88565fe..43e0d0d7e 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockRichText/BlockRichText.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockRichText/BlockRichText.tsx @@ -84,7 +84,7 @@ export const BlockRichText: FunctionComponent = ({ = ({ !isNil(link) ? ( {label} ) : ( -

+

{label}

); diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockThemeReels/BlockThemeReelSection.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockThemeReels/BlockThemeReelSection.tsx index bdaab9a32..2cfdcc699 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockThemeReels/BlockThemeReelSection.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockThemeReels/BlockThemeReelSection.tsx @@ -121,10 +121,12 @@ export const BlockThemeReelSection: FunctionComponent {title && ( - {title} + + {title} + )} {description && ( - + {description} )} @@ -136,9 +138,11 @@ export const BlockThemeReelSection: FunctionComponent
- {themeName} + + {themeName} + {isMobileWidth() && (description || themeDescription) && ( - + {description || themeDescription || ''} )} @@ -297,7 +301,7 @@ export const BlockThemeReelSection: FunctionComponent
- + {tText( 'modules/content-page/components/blocks/block-theme-reels/block-theme-reel-section___toon-alle-materialen-voor-dit-theme', {}, diff --git a/ui/src/react-admin/modules/content-page/components/blocks/BlockVideoTitleTextButton/BlockVideoTitleTextButton.tsx b/ui/src/react-admin/modules/content-page/components/blocks/BlockVideoTitleTextButton/BlockVideoTitleTextButton.tsx index 721e977a9..2d8104644 100644 --- a/ui/src/react-admin/modules/content-page/components/blocks/BlockVideoTitleTextButton/BlockVideoTitleTextButton.tsx +++ b/ui/src/react-admin/modules/content-page/components/blocks/BlockVideoTitleTextButton/BlockVideoTitleTextButton.tsx @@ -45,13 +45,21 @@ export const BlockVideoTitleTextButton: FunctionComponent {title && (

- + {title}

)} {text && ( - + )}
diff --git a/ui/src/react-admin/modules/content-page/const/background-text-colors.test.ts b/ui/src/react-admin/modules/content-page/const/background-text-colors.test.ts new file mode 100644 index 000000000..8a6a5953f --- /dev/null +++ b/ui/src/react-admin/modules/content-page/const/background-text-colors.test.ts @@ -0,0 +1,159 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { Color, CustomBackground, GradientColor } from '../types/content-block.types'; +import { + BACKGROUND_TEXT_COLORS, + getBackgroundTextColors, + getBackgroundTextColorVariables, +} from './background-text-colors'; +import { GET_BACKGROUND_COLOR_OPTIONS_ARCHIEF, hasDarkBackground } from './get-color-options'; + +const isAvoMock = vi.hoisted(() => vi.fn<() => boolean>()); + +vi.mock('~shared/helpers/is-avo', () => ({ isAvo: isAvoMock })); +vi.mock('~shared/helpers/translation-functions', () => ({ tText: (key: string) => key })); + +/** + * Every supported Archief background as [name, background, primary, secondary, hyperlink], based + * on meemoo-hetarchief-kleurencombinaties.pdf and the corrections confirmed by meemoo on + * ARC-3848. Sky blauw is the only selectable legacy color that is absent from the PDF. + */ +const EXPECTED_BACKGROUND_TEXT_COLORS: [string, Color, Color, Color?, Color?][] = [ + // Merk + ['Zwart', Color.Black, Color.White, Color.Zinc, Color.OceanGreen], + ['Wit', Color.White, Color.Black, Color.Slate, Color.Jade], + ['Teal', Color.OceanGreen, Color.Black], + // Functioneel + ['Grafiet', Color.Graphite, Color.White, Color.Zinc, Color.OceanGreen], + ['Inkt', Color.Ink, Color.White, Color.Zinc, Color.OceanGreen], + ['Schaduw', Color.Shadow, Color.White], + ['Leisteen', Color.Slate, Color.White], + ['Neutraal', Color.ArchiefNeutral, Color.White], + ['Zink', Color.Zinc, Color.Black], + ['Zilver', Color.ArchiefSilver, Color.Black, Color.Slate, Color.Lagoon], + ['Platinum', Color.Platinum, Color.Black, Color.Slate, Color.Lagoon], + ['Kers', Color.Cherry, Color.White], + ['Jade', Color.Jade, Color.White, Color.Black], + ['Lagune', Color.Lagoon, Color.White], + // Secundair + ['Zeegroen', Color.SeaGreen, Color.Black], + ['Grasgroen', Color.GrassGreen, Color.Black], + ['Azuur', Color.Azure, Color.Black], + // Tertiair + ['Lila', Color.Lila, Color.Black], + ['Mosterd', Color.Mustard, Color.Black], + ['Koraal', Color.Coral, Color.Black], + ['Baby blauw', Color.BabyBlue, Color.Black, undefined, Color.Lagoon], + ['Blush', Color.BlossomPink, Color.Black], + ['Donker lila', Color.Lavender, Color.Black], + ['Mist', Color.Sage, Color.Black], + ['Sepia', Color.SandBeige, Color.Black], + ['Mauve', Color.OldPink, Color.White], + ['Salie', Color.Pistachio, Color.Black], + ['Terra', Color.Terra, Color.Black], + ['Olijf', Color.Olive, Color.White], + ['Viool', Color.Viola, Color.White], + // Not in the PDF; temporarily follows Baby blauw while meemoo decides whether it remains. + ['Sky blauw', Color.SkyBlue, Color.Black, undefined, Color.Lagoon], +]; + +describe('getBackgroundTextColors()', () => { + beforeEach(() => { + isAvoMock.mockReturnValue(false); + }); + + it.each(EXPECTED_BACKGROUND_TEXT_COLORS)( + 'matches the design for %s', + (_name, background, primary, secondary, hyperlink) => { + expect(getBackgroundTextColors(background)).toEqual({ + primary, + ...(secondary ? { secondary } : {}), + ...(hyperlink ? { hyperlink } : {}), + }); + } + ); + + it('holds every approved row and the temporary Sky blauw fallback, with no extras', () => { + expect(Object.keys(BACKGROUND_TEXT_COLORS)).toHaveLength( + EXPECTED_BACKGROUND_TEXT_COLORS.length + ); + }); + + it('looks up the Color enum values directly', () => { + expect(getBackgroundTextColors(Color.Black)?.primary).toBe(Color.White); + expect(getBackgroundTextColors(Color.White)?.primary).toBe(Color.Black); + expect(getBackgroundTextColors(Color.Lila)?.primary).toBe(Color.Black); + expect(getBackgroundTextColors(Color.OldPink)?.primary).toBe(Color.White); + expect(getBackgroundTextColors(Color.BabyBlue)?.hyperlink).toBe(Color.Lagoon); + expect(getBackgroundTextColors(Color.SkyBlue)).toEqual(getBackgroundTextColors(Color.BabyBlue)); + }); + + it('has a ruling for every selectable flat Archief background', () => { + const backgroundsWithoutOneTextColor = [ + Color.Transparent, + GradientColor.BlackWhite, + CustomBackground.MeemooLogo, + ]; + const flatBackgrounds = GET_BACKGROUND_COLOR_OPTIONS_ARCHIEF() + .map((option) => option.value) + .filter((value) => !backgroundsWithoutOneTextColor.includes(value)); + + for (const background of flatBackgrounds) { + expect( + getBackgroundTextColors(background), + `missing text colors for ${background}` + ).toBeDefined(); + } + }); + + // Design specified nothing for these, so blocks keep whatever they inherit. In particular, + // meemoo confirmed that BlackWhite must retain the existing per-block handling. + it.each<[string, Color | GradientColor | CustomBackground | undefined]>([ + ['transparent', Color.Transparent], + ['the separately handled black-white gradient', GradientColor.BlackWhite], + ['the meemoo logo pattern', CustomBackground.MeemooLogo], + ['an AVO-only color', Color.SoftBlue], + ['no background', undefined], + ])('has no colors for %s', (_name, background) => { + expect(getBackgroundTextColors(background)).toBeUndefined(); + }); + + describe('on AVO', () => { + beforeEach(() => { + isAvoMock.mockReturnValue(true); + }); + + it('does not apply an Archief record to a shared hex color', () => { + expect(getBackgroundTextColors(Color.OceanGreen)).toBeUndefined(); + expect(getBackgroundTextColorVariables(Color.OceanGreen)).toEqual({}); + }); + + it('keeps the existing AVO dark-background ruling', () => { + expect(hasDarkBackground(Color.OceanGreen)).toBe(true); + }); + }); +}); + +describe('getBackgroundTextColorVariables()', () => { + beforeEach(() => { + isAvoMock.mockReturnValue(false); + }); + it('exposes all three roles when design specified all three', () => { + expect(getBackgroundTextColorVariables(Color.Black)).toEqual({ + '--bg-text-primary': Color.White, + '--bg-text-secondary': Color.Zinc, + '--bg-text-hyperlink': Color.OceanGreen, + }); + }); + + // Leaving them unset is what makes the utility classes fall back to the primary color. + it('omits the roles design did not specify', () => { + expect(getBackgroundTextColorVariables(Color.OldPink)).toEqual({ + '--bg-text-primary': Color.White, + }); + }); + + it('returns nothing for a background design specified no colors for', () => { + expect(getBackgroundTextColorVariables(GradientColor.BlackWhite)).toEqual({}); + }); +}); diff --git a/ui/src/react-admin/modules/content-page/const/background-text-colors.ts b/ui/src/react-admin/modules/content-page/const/background-text-colors.ts new file mode 100644 index 000000000..ccd4ae36e --- /dev/null +++ b/ui/src/react-admin/modules/content-page/const/background-text-colors.ts @@ -0,0 +1,139 @@ +import { isAvo } from '~shared/helpers/is-avo'; +import { Color, type CustomBackground, type GradientColor } from '../types/content-block.types'; + +/** + * The WCAG text colors per background color, based on + * meemoo-hetarchief-kleurencombinaties.pdf (attached to + * https://meemoo.atlassian.net/browse/ARC-3848) and meemoo's confirmed corrections. + * + * Every content block that renders text on an admin-picked background color, and does not offer a + * text color field of its own, takes its text colors from here. + * + * The columns map to `primary` (body text), `secondary` (muted text such as captions, subtitles + * and metadata) and `hyperlink` (the underlined link color). Differences from the PDF must be + * documented at the affected row. + */ + +export const TEXT_COLOR_WHITE = Color.White; + +export interface BackgroundTextColors { + /** Body text. Always present. */ + primary: Color; + /** Muted text: captions, subtitles, metadata. Absent when design specified no second color. */ + secondary?: Color; + /** Underlined link text. Absent when design specified no link color. */ + hyperlink?: Color; +} + +/** + * Only Het Archief colors have a record; the shared Color enum also contains AVO colors. + */ +export const BACKGROUND_TEXT_COLORS: Partial> = { + // Merk + [Color.Black]: { + primary: Color.White, + secondary: Color.Zinc, + hyperlink: Color.OceanGreen, + }, // Zwart + // Meemoo replaced Neutraal #757575 with Leisteen #666666 for muted text on white. + [Color.White]: { primary: Color.Black, secondary: Color.Slate, hyperlink: Color.Jade }, // Wit + [Color.OceanGreen]: { primary: Color.Black }, // Teal + + // Functioneel + [Color.Graphite]: { + primary: Color.White, + secondary: Color.Zinc, + hyperlink: Color.OceanGreen, + }, // Grafiet + [Color.Ink]: { + primary: Color.White, + secondary: Color.Zinc, + hyperlink: Color.OceanGreen, + }, // Inkt + [Color.Shadow]: { primary: Color.White }, // Schaduw + [Color.Slate]: { primary: Color.White }, // Leisteen + [Color.ArchiefNeutral]: { primary: Color.White }, // Neutraal + // The PDF text originally listed white, but meemoo confirmed the visual is authoritative: black. + [Color.Zinc]: { primary: Color.Black }, // Zink + [Color.ArchiefSilver]: { + primary: Color.Black, + secondary: Color.Slate, + hyperlink: Color.Lagoon, + }, // Zilver + [Color.Platinum]: { + primary: Color.Black, + secondary: Color.Slate, + hyperlink: Color.Lagoon, + }, // Platinum + [Color.Cherry]: { primary: Color.White }, // Kers + // Jade lists a second color (Zwart) that is not underlined in the PDF, so it reads as secondary + // rather than a link color. Confirm with design. + [Color.Jade]: { primary: Color.White, secondary: Color.Black }, // Jade + [Color.Lagoon]: { primary: Color.White }, // Lagune + + // Secundair + [Color.SeaGreen]: { primary: Color.Black }, // Zeegroen + [Color.GrassGreen]: { primary: Color.Black }, // Grasgroen + [Color.Azure]: { primary: Color.Black }, // Azuur + + // Tertiair + [Color.Lila]: { primary: Color.Black }, // Lila + [Color.Mustard]: { primary: Color.Black }, // Mosterd + [Color.Coral]: { primary: Color.Black }, // Koraal + // Baby blauw's second color IS underlined in the PDF, so it is the link color, not secondary. + [Color.BabyBlue]: { primary: Color.Black, hyperlink: Color.Lagoon }, // Baby blauw + [Color.BlossomPink]: { primary: Color.Black }, // Blush + [Color.Lavender]: { primary: Color.Black }, // Donker lila + [Color.Sage]: { primary: Color.Black }, // Mist + [Color.SandBeige]: { primary: Color.Black }, // Sepia + [Color.OldPink]: { primary: Color.White }, // Mauve / "oud roze" + [Color.Pistachio]: { primary: Color.Black }, // Salie + [Color.Terra]: { primary: Color.Black }, // Terra + [Color.Olive]: { primary: Color.White }, // Olijf + [Color.Viola]: { primary: Color.White }, // Viool + + // Selectable legacy color that is not in the PDF. Until meemoo decides whether Sky blauw stays, + // it uses the confirmed Baby blauw text colors. + [Color.SkyBlue]: { primary: Color.Black, hyperlink: Color.Lagoon }, // Sky blauw +}; + +/** + * The Archief text colors specified for this background color, or undefined on AVO and when the + * background is not a flat color from the palette (transparent, a gradient or the meemoo logo + * pattern). Meemoo explicitly confirmed that the BlackWhite gradient must keep each block's + * existing, separately handled text styling. AVO follows its own brand book, including for hex + * values shared by both apps. + */ +export function getBackgroundTextColors( + color: Color | GradientColor | CustomBackground | undefined +): BackgroundTextColors | undefined { + if (!color || isAvo()) { + return undefined; + } + + return BACKGROUND_TEXT_COLORS[color as Color]; +} + +/** + * The design text colors for this background as css variables, to spread into a style prop. The + * u-background-text-* classes read these, so any element inside can say which role its text + * plays instead of hardcoding a color. + * + * Returns an empty object when design specified nothing for this background. The renderer then + * omits the u-background-text-colors wrapper, leaving the role classes inactive. + */ +export function getBackgroundTextColorVariables( + color: Color | GradientColor | CustomBackground | undefined +): Record { + const textColors = getBackgroundTextColors(color); + + if (!textColors) { + return {}; + } + + return { + '--bg-text-primary': textColors.primary, + ...(textColors.secondary ? { '--bg-text-secondary': textColors.secondary } : {}), + ...(textColors.hyperlink ? { '--bg-text-hyperlink': textColors.hyperlink } : {}), + }; +} diff --git a/ui/src/react-admin/modules/content-page/const/get-color-options.ts b/ui/src/react-admin/modules/content-page/const/get-color-options.ts index 2329efa28..240b8fd67 100644 --- a/ui/src/react-admin/modules/content-page/const/get-color-options.ts +++ b/ui/src/react-admin/modules/content-page/const/get-color-options.ts @@ -1,8 +1,10 @@ import type { SelectOption } from '@viaa/avo2-components'; +import { isAvo } from '~shared/helpers/is-avo'; import { tText } from '~shared/helpers/translation-functions'; import { AVO } from '~shared/types'; import { App } from '../../../../../scripts/translation.types'; import { Color, CustomBackground, GradientColor } from '../types/content-block.types'; +import { getBackgroundTextColors, TEXT_COLOR_WHITE } from './background-text-colors'; const transparentOption = () => ({ label: tText('admin/content-block/content-block___geen'), @@ -96,9 +98,9 @@ const coralOption = () => ({ label: tText('modules/content-page/const/content-block___koraal-oranje', {}, [App.HET_ARCHIEF]), value: Color.Coral, }); -const lightBlueOption = () => ({ - label: tText('modules/content-page/const/content-block___poederblauw', {}, [App.HET_ARCHIEF]), - value: Color.LightBlue, +const babyBlueOption = () => ({ + label: tText('modules/content-page/const/content-block___babyblauw', {}, [App.HET_ARCHIEF]), + value: Color.BabyBlue, }); const sageOption = () => ({ label: tText('modules/content-page/const/content-block___salie-groen', {}, [App.HET_ARCHIEF]), @@ -130,15 +132,13 @@ export const GET_BACKGROUND_COLOR_OPTIONS_AVO: () => SelectOption[] = () yellowOption(), ]; -export const GET_SECONDARY_BACKGROUND_COLOR_OPTIONS_ARCHIEF: () => SelectOption< - Color | GradientColor | CustomBackground ->[] = () => [ +export const GET_SECONDARY_BACKGROUND_COLOR_OPTIONS_ARCHIEF: () => SelectOption[] = () => [ oldPinkOption(), lavenderOption(), lilaOption(), blossomPinkOption(), coralOption(), - lightBlueOption(), + babyBlueOption(), sageOption(), pistachioOption(), sandBeigeOption(), @@ -169,17 +169,38 @@ export const GET_AVO_HERO_BACKGROUND_COLOR_OPTIONS: () => SelectOption[] yellowOption(), ]; -export const GET_DARK_BACKGROUND_COLOR_OPTIONS: () => (Color | GradientColor | CustomBackground)[] = - () => [ - Color.SoftBlue, - Color.NightBlue, - Color.Teal, - Color.TealBright, - Color.OceanGreen, - Color.SeaGreen, - Color.Yellow, - Color.Black, - ]; +export const DARK_BACKGROUND_COLOR_OPTIONS_AVO: (Color | GradientColor | CustomBackground)[] = [ + Color.SoftBlue, + Color.NightBlue, + Color.Teal, + Color.TealBright, + Color.OceanGreen, + Color.SeaGreen, + Color.Yellow, + Color.Black, +]; + +/** + * Whether this background needs light text, so blocks can pick a dark-background variant of their + * styling. On archief the answer comes from the design record in background-text-colors.ts; AVO + * keeps its own list, since its palette follows a different brand book. + * + * Prefer getBackgroundTextColors() where you need the actual colors - this only answers "is it a + * dark background", not "which color is the text". https://meemoo.atlassian.net/browse/ARC-3848 + */ +export function hasDarkBackground( + color: Color | GradientColor | CustomBackground | undefined +): boolean { + if (!color) { + return false; + } + + if (isAvo()) { + return DARK_BACKGROUND_COLOR_OPTIONS_AVO.includes(color as Color); + } + + return getBackgroundTextColors(color)?.primary === TEXT_COLOR_WHITE; +} export const GET_FOREGROUND_COLOR_OPTIONS_AVO: () => SelectOption[] = () => [ { diff --git a/ui/src/react-admin/modules/content-page/types/content-block.types.ts b/ui/src/react-admin/modules/content-page/types/content-block.types.ts index f276f3ce0..9871b4bb9 100644 --- a/ui/src/react-admin/modules/content-page/types/content-block.types.ts +++ b/ui/src/react-admin/modules/content-page/types/content-block.types.ts @@ -95,13 +95,29 @@ export enum Color { SkyBlue = '#C3DDE6', Juniper = '#678588', + // Het Archief colors from the approved color-combination document. + Graphite = '#222222', + Ink = '#303030', + Shadow = '#505050', + Slate = '#666666', + ArchiefNeutral = '#757575', + ArchiefSilver = '#E6E6E6', + Cherry = '#D60039', + Jade = '#00857D', + Lagoon = '#005F69', + GrassGreen = '#82E678', + Azure = '#28A0C8', + Terra = '#D1543A', + Olive = '#64702B', + Viola = '#432457', + // Tertiary colors OldPink = '#9B6072', Lavender = '#A293AF', Lila = '#c6c2e0', BlossomPink = '#E694B3', Coral = '#E89B88', - LightBlue = '#BDDEE7', + BabyBlue = '#8DDEE7', Sage = '#91A9A7', Pistachio = '#B8BE9A', SandBeige = '#EDD6C4', diff --git a/ui/src/react-admin/modules/shared/components/CopyrightAttribution/CopyrightAttribution.test.tsx b/ui/src/react-admin/modules/shared/components/CopyrightAttribution/CopyrightAttribution.test.tsx new file mode 100644 index 000000000..5e175eb6e --- /dev/null +++ b/ui/src/react-admin/modules/shared/components/CopyrightAttribution/CopyrightAttribution.test.tsx @@ -0,0 +1,18 @@ +import { cleanup, render, screen } from '@testing-library/react'; +import React from 'react'; +import { afterEach, describe, expect, it } from 'vitest'; +import { CopyrightAttribution } from './CopyrightAttribution'; + +afterEach(() => cleanup()); + +describe('', () => { + it('marks the annotation as secondary and the attribution text as primary', () => { + const { container } = render( + + ); + + expect(screen.getByText(/Photographer/)).toHaveClass('u-background-text-secondary'); + expect(screen.getByText('Collection')).toHaveClass('u-background-text-primary'); + expect(container.firstChild).toHaveClass('a-copyright-attribution'); + }); +}); diff --git a/ui/src/react-admin/modules/shared/components/CopyrightAttribution/CopyrightAttribution.tsx b/ui/src/react-admin/modules/shared/components/CopyrightAttribution/CopyrightAttribution.tsx index 31bed4980..b0a32d63b 100644 --- a/ui/src/react-admin/modules/shared/components/CopyrightAttribution/CopyrightAttribution.tsx +++ b/ui/src/react-admin/modules/shared/components/CopyrightAttribution/CopyrightAttribution.tsx @@ -27,7 +27,7 @@ export const CopyrightAttribution: FunctionComponent } return ( - + {showIcon && <>©} {title} ); @@ -36,7 +36,9 @@ export const CopyrightAttribution: FunctionComponent return (
{renderTitle()} - {text && {text}} + {text && ( + {text} + )}
); }; diff --git a/ui/src/react-admin/modules/shared/styles/utilities/_background-text.scss b/ui/src/react-admin/modules/shared/styles/utilities/_background-text.scss new file mode 100644 index 000000000..b3abf71a8 --- /dev/null +++ b/ui/src/react-admin/modules/shared/styles/utilities/_background-text.scss @@ -0,0 +1,26 @@ +/* Semantic text-role classes for content blocks whose foreground follows an editor-selected + background. Kept in a mixin so the same contract can be emitted by admin.css and client.css. */ +@mixin background-text-roles { + .u-background-text-colors { + color: var(--bg-text-primary) !important; + + .u-background-text-primary { + color: var(--bg-text-primary) !important; + } + + .u-background-text-secondary { + color: var(--bg-text-secondary, var(--bg-text-primary)) !important; + } + + .u-background-text-hyperlink, + .u-background-text-links a:not(.a-link__no-styles) { + color: var(--bg-text-hyperlink, var(--bg-text-primary)) !important; + } + + /* SmartLink deliberately removes link styling; it should inherit the semantic text role of its + contents instead of the application's legacy global black link override. */ + .a-link__no-styles { + color: inherit; + } + } +} diff --git a/ui/src/react-admin/modules/shared/styles/utilities/_color.scss b/ui/src/react-admin/modules/shared/styles/utilities/_color.scss index 93399a2e2..bc859edba 100644 --- a/ui/src/react-admin/modules/shared/styles/utilities/_color.scss +++ b/ui/src/react-admin/modules/shared/styles/utilities/_color.scss @@ -1,4 +1,5 @@ @use '../../styles/settings/colors' as colors; +@use './background-text' as background-text; /* ========================================================================== Utility: Color @@ -11,3 +12,7 @@ .u-color-gray-150 { color: colors.$color-gray-150 !important; } + +// The complete utility index feeds admin.css. ContentBlockRenderer includes the same mixin in the +// public client bundle, whose style graph intentionally excludes this index. +@include background-text.background-text-roles; diff --git a/ui/src/shared/translations/hetArchief/nl.json b/ui/src/shared/translations/hetArchief/nl.json index 88621ca2d..09f33fa8b 100644 --- a/ui/src/shared/translations/hetArchief/nl.json +++ b/ui/src/shared/translations/hetArchief/nl.json @@ -784,6 +784,7 @@ "modules/content-page/components/content-page-renderer/content-page-renderer___bewerk-pagina": "Bewerk pagina", "modules/content-page/components/content-page-renderer/content-page-renderer___bewerk-pagina-tooltip": "Bewerk pagina tooltip", "modules/content-page/components/date-picker/date-picker___datum-input-aria-label": "Kies hier de datum", + "modules/content-page/const/content-block___babyblauw": "Baby blauw", "modules/content-page/const/content-block___bloesem-roze": "Bloesem roze", "modules/content-page/const/content-block___honing-geel": "Honing geel", "modules/content-page/const/content-block___koraal-oranje": "Koraal oranje", @@ -794,7 +795,6 @@ "modules/content-page/const/content-block___overgang-zwart-wit": "Overgang zwart wit", "modules/content-page/const/content-block___pistache-groen": "Pistache groen", "modules/content-page/const/content-block___platinum": "Platinum", - "modules/content-page/const/content-block___poederblauw": "Poederblauw", "modules/content-page/const/content-block___salie-groen": "Salie groen", "modules/content-page/const/content-block___sky-blauw": "Sky blauw", "modules/content-page/const/content-block___zand-beige": "Zand beige",