Share the tip card shell and the leave-type swatch styles
TipCardShell replaces the tip-card markup that PlanWarningCard, SurplusAdviceTip and VacationRescueTip each copied from TipCard. The vacation/sjukpenning/lägsta fills and cap marks move to .leave-swatch--* in primitives.css; the calendar band, year rows and help popover keep only their size, position and deliberate overrides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,10 +16,22 @@ const DAY_MARKS: ReadonlyArray<{ key: string; className: string; labelKey: Trans
|
||||
/** The band styles: type by fill/ink/outline, owner by the left cap. */
|
||||
const BAND_MARKS: ReadonlyArray<{ key: string; className: string; labelKey: TranslationKey; text?: string }> = [
|
||||
{ key: 'vacation', className: 'calendar-help__band calendar-help__band--vacation', labelKey: 'calendar.helpVacation' },
|
||||
{ key: 'sjukpenning', className: 'calendar-help__band calendar-help__band--sjukpenning', labelKey: 'calendar.helpSjukpenning' },
|
||||
{ key: 'lagsta', className: 'calendar-help__band calendar-help__band--lagsta', labelKey: 'calendar.helpLagsta' },
|
||||
{ key: 'self', className: 'calendar-help__band calendar-help__band--self', labelKey: 'calendar.helpSelf' },
|
||||
{ key: 'partner', className: 'calendar-help__band calendar-help__band--partner', labelKey: 'calendar.helpPartner' },
|
||||
{
|
||||
key: 'sjukpenning',
|
||||
className: 'calendar-help__band calendar-help__band--sjukpenning leave-swatch--sjukpenning',
|
||||
labelKey: 'calendar.helpSjukpenning',
|
||||
},
|
||||
{
|
||||
key: 'lagsta',
|
||||
className: 'calendar-help__band calendar-help__band--lagsta leave-swatch--lagsta',
|
||||
labelKey: 'calendar.helpLagsta',
|
||||
},
|
||||
{ key: 'self', className: 'calendar-help__band calendar-help__band--self leave-swatch--cap-self', labelKey: 'calendar.helpSelf' },
|
||||
{
|
||||
key: 'partner',
|
||||
className: 'calendar-help__band calendar-help__band--partner leave-swatch--cap-partner',
|
||||
labelKey: 'calendar.helpPartner',
|
||||
},
|
||||
{ key: 'dubbeldag', className: 'calendar-help__band calendar-help__band--dubbeldag', labelKey: 'calendar.helpDubbeldag', text: '2×' },
|
||||
{ key: 'pinned', className: 'calendar-help__band calendar-help__band--pinned', labelKey: 'calendar.helpPinned' },
|
||||
]
|
||||
|
||||
@@ -127,30 +127,19 @@
|
||||
color: var(--c-leave-vacation-ink);
|
||||
}
|
||||
|
||||
/* Fill/hatch/outline and the cap-mark shadow are the shared
|
||||
* `.leave-swatch--*` recipe from primitives.css (added alongside these
|
||||
* classes in CalendarHelpPopover.tsx); ink stays local since it isn't part
|
||||
* of that shared recipe (the calendar band and year-row swatch don't set
|
||||
* it via CSS either — see leaveTypes.ts's `getInkForType`). */
|
||||
.calendar-help__band--sjukpenning {
|
||||
background-color: var(--c-leave-sjukpenning);
|
||||
color: var(--c-leave-sjukpenning-ink);
|
||||
}
|
||||
|
||||
.calendar-help__band--lagsta {
|
||||
background-color: var(--c-leave-lagsta);
|
||||
background-image: repeating-linear-gradient(
|
||||
135deg,
|
||||
transparent 0 4px,
|
||||
var(--c-leave-lagsta-hatch) 4px 5px
|
||||
);
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline);
|
||||
color: var(--c-leave-lagsta-ink);
|
||||
}
|
||||
|
||||
.calendar-help__band--self {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-self);
|
||||
}
|
||||
|
||||
.calendar-help__band--partner {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-partner);
|
||||
}
|
||||
|
||||
.calendar-help__band--pinned {
|
||||
box-shadow: inset 0 -2px 0 var(--c-leave-vacation-ink);
|
||||
}
|
||||
|
||||
@@ -402,10 +402,15 @@ export function CalendarWeek({
|
||||
// see the --c-leave-* tokens) and owner by a 2px left cap
|
||||
// (`--self`/`--partner`) — never by hue, so the two axes stay
|
||||
// readable side by side and in greyscale.
|
||||
const typeKey = getTypeKey(representativeType)
|
||||
const classNames = [
|
||||
'calendar-band',
|
||||
`calendar-band--${getTypeKey(representativeType)}`,
|
||||
`calendar-band--${typeKey}`,
|
||||
// `leave-swatch--*` (primitives.css) carries the shared
|
||||
// fill/hatch/outline/cap-mark recipe; `unknown` has no recipe.
|
||||
typeKey !== 'unknown' ? `leave-swatch--${typeKey}` : '',
|
||||
segment.parentUuid === personalDataUuid ? 'calendar-band--self' : 'calendar-band--partner',
|
||||
segment.parentUuid === personalDataUuid ? 'leave-swatch--cap-self' : 'leave-swatch--cap-partner',
|
||||
segment.isBandStart ? 'calendar-band--start' : '',
|
||||
segment.isBandEnd ? 'calendar-band--end' : '',
|
||||
segment.wishId && segment.wishId === flashWishId ? 'calendar-band--flash' : '',
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { TipCardShell } from './TipCardShell'
|
||||
|
||||
describe('TipCardShell', () => {
|
||||
it.each([
|
||||
['info', 'tip-card--info'],
|
||||
['warning', 'tip-card--warning'],
|
||||
['action', 'tip-card--action'],
|
||||
['danger', 'tip-card--danger'],
|
||||
] as const)('renders the %s variant with role="article" and the matching class', (variant, cls) => {
|
||||
render(
|
||||
<TipCardShell variant={variant} title="A title">
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
const el = screen.getByRole('article')
|
||||
expect(el).toHaveClass('tip-card', cls)
|
||||
})
|
||||
|
||||
it('renders the title in a tip-card__title heading and the children below the header', () => {
|
||||
render(
|
||||
<TipCardShell variant="info" title="My title">
|
||||
<p className="tip-card__body">My body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
const el = screen.getByRole('article')
|
||||
expect(el.querySelector('.tip-card__title')?.textContent).toBe('My title')
|
||||
expect(el.querySelector('.tip-card__body')?.textContent).toBe('My body')
|
||||
})
|
||||
|
||||
it('renders without a data-testid when none is given', () => {
|
||||
render(
|
||||
<TipCardShell variant="info" title="T">
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
expect(screen.getByRole('article')).not.toHaveAttribute('data-testid')
|
||||
})
|
||||
|
||||
it('applies a testId when given', () => {
|
||||
render(
|
||||
<TipCardShell variant="info" title="T" testId="my-tip">
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
expect(screen.getByTestId('my-tip')).toBe(screen.getByRole('article'))
|
||||
})
|
||||
|
||||
it('applies the aria-label when given', () => {
|
||||
render(
|
||||
<TipCardShell variant="info" title="T" ariaLabel="Tips: T">
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
expect(screen.getByRole('article')).toHaveAttribute('aria-label', 'Tips: T')
|
||||
})
|
||||
|
||||
it('appends a custom className alongside the variant class', () => {
|
||||
render(
|
||||
<TipCardShell variant="action" title="T" className="vacation-rescue-tip">
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
expect(screen.getByRole('article')).toHaveClass('tip-card', 'tip-card--action', 'vacation-rescue-tip')
|
||||
})
|
||||
|
||||
it('does not render a dismiss button when onDismiss is not provided', () => {
|
||||
render(
|
||||
<TipCardShell variant="info" title="T">
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
expect(screen.queryByTestId('dismiss-button')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders a dismiss button with the given label and calls onDismiss when clicked', () => {
|
||||
const onDismiss = vi.fn()
|
||||
render(
|
||||
<TipCardShell variant="info" title="T" onDismiss={onDismiss} dismissLabel="Dismiss T">
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
const button = screen.getByTestId('dismiss-button')
|
||||
expect(button).toHaveAttribute('aria-label', 'Dismiss T')
|
||||
fireEvent.click(button)
|
||||
expect(onDismiss).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('renders headerExtra content instead of relying on the generic dismiss button', () => {
|
||||
render(
|
||||
<TipCardShell variant="action" title="T" headerExtra={<button data-testid="custom-dismiss">Bye</button>}>
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
expect(screen.getByTestId('custom-dismiss')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('dismiss-button')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders both the generic dismiss button and headerExtra when both are given', () => {
|
||||
render(
|
||||
<TipCardShell
|
||||
variant="info"
|
||||
title="T"
|
||||
onDismiss={() => {}}
|
||||
headerExtra={<span data-testid="extra">Extra</span>}
|
||||
>
|
||||
<p>Body</p>
|
||||
</TipCardShell>,
|
||||
)
|
||||
expect(screen.getByTestId('dismiss-button')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('extra')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export type TipCardVariant = 'info' | 'warning' | 'action' | 'danger'
|
||||
|
||||
export interface TipCardShellProps {
|
||||
variant: TipCardVariant
|
||||
title: ReactNode
|
||||
children: ReactNode
|
||||
/** Renders the generic "×" dismiss button (see `TipCard`'s own use) when
|
||||
* provided. Omit this and use `headerExtra` instead when a consumer needs
|
||||
* a differently-styled header control, e.g. `VacationRescueTip`'s labelled
|
||||
* dismiss button with its own testid/disabled state. */
|
||||
onDismiss?: (() => void) | undefined
|
||||
/** aria-label for the generic dismiss button; ignored without `onDismiss`. */
|
||||
dismissLabel?: string | undefined
|
||||
/** Custom content rendered in the header after the title, in place of the
|
||||
* generic dismiss button. */
|
||||
headerExtra?: ReactNode
|
||||
testId?: string | undefined
|
||||
ariaLabel?: string | undefined
|
||||
className?: string | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* The `tip-card` / `tip-card__header` / `tip-card__title` shell shared by
|
||||
* `TipCard` and the dashboard's derived tip-like cards (`PlanWarningCard`,
|
||||
* `SurplusAdviceTip`, `VacationRescueTip`) — so the dashboard reads as one
|
||||
* visual system. Body content (and anything beyond it, e.g.
|
||||
* `VacationRescueTip`'s date list) is left to `children`, since it varies
|
||||
* per consumer.
|
||||
*/
|
||||
export function TipCardShell({
|
||||
variant,
|
||||
title,
|
||||
children,
|
||||
onDismiss,
|
||||
dismissLabel,
|
||||
headerExtra,
|
||||
testId,
|
||||
ariaLabel,
|
||||
className,
|
||||
}: TipCardShellProps): ReactNode {
|
||||
return (
|
||||
<div
|
||||
data-testid={testId}
|
||||
className={className ? `tip-card tip-card--${variant} ${className}` : `tip-card tip-card--${variant}`}
|
||||
role="article"
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<div className="tip-card__header">
|
||||
<h4 className="tip-card__title">{title}</h4>
|
||||
{onDismiss && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn--icon tip-card__dismiss"
|
||||
onClick={onDismiss}
|
||||
aria-label={dismissLabel}
|
||||
data-testid="dismiss-button"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
{headerExtra}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { ReactNode } from 'react'
|
||||
import type { PlanWarning } from '@/types'
|
||||
import { useTranslation, toTranslationParams, type TranslationKey } from '@/i18n'
|
||||
import { formatDateForDisplay } from '@/utils/dates'
|
||||
import { TipCardShell } from '@/components/common/TipCardShell'
|
||||
|
||||
// The three `PlanWarning` kinds the backend can emit
|
||||
// (`allocation_service.py`'s `_sgi_risk_warnings`, `_age4_warnings`,
|
||||
@@ -88,16 +89,13 @@ export function PlanWarningCard({ warning, nameFor, index }: PlanWarningCardProp
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="tip-card tip-card--danger"
|
||||
role="article"
|
||||
aria-label={t('tip.aria', { title })}
|
||||
data-testid={`dashboard-plan-warning-${index}`}
|
||||
<TipCardShell
|
||||
variant="danger"
|
||||
title={title}
|
||||
testId={`dashboard-plan-warning-${index}`}
|
||||
ariaLabel={t('tip.aria', { title })}
|
||||
>
|
||||
<div className="tip-card__header">
|
||||
<h4 className="tip-card__title">{title}</h4>
|
||||
</div>
|
||||
<p className="tip-card__body">{body}</p>
|
||||
</div>
|
||||
</TipCardShell>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ReactNode } from 'react'
|
||||
import type { SurplusAdvice } from '@/types'
|
||||
import { useTranslation, toTranslationParams, type TranslationKey } from '@/i18n'
|
||||
import { TipCardShell } from '@/components/common/TipCardShell'
|
||||
|
||||
// §9.2 surplus-advice kinds — `kind` -> title, `messageKey` -> the body
|
||||
// template that interpolates the advice's own `data`. An unrecognised kind
|
||||
@@ -51,16 +52,13 @@ export function SurplusAdviceTip({ advice, nameFor, index }: SurplusAdviceTipPro
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="tip-card tip-card--action"
|
||||
role="article"
|
||||
aria-label={t('tip.aria', { title })}
|
||||
data-testid={`dashboard-surplus-advice-${index}`}
|
||||
<TipCardShell
|
||||
variant="action"
|
||||
title={title}
|
||||
testId={`dashboard-surplus-advice-${index}`}
|
||||
ariaLabel={t('tip.aria', { title })}
|
||||
>
|
||||
<div className="tip-card__header">
|
||||
<h4 className="tip-card__title">{title}</h4>
|
||||
</div>
|
||||
<p className="tip-card__body">{body}</p>
|
||||
</div>
|
||||
</TipCardShell>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { RescueDay } from '@/types'
|
||||
import { useTranslation } from '@/i18n'
|
||||
import { useRescueDayActions, RescueDismissedHandler } from '@/hooks/useRescueDayActions'
|
||||
import { formatDateForDisplay } from '@/utils/dates'
|
||||
import { TipCardShell } from '@/components/common/TipCardShell'
|
||||
|
||||
export interface VacationRescueTipProps {
|
||||
workspaceId: string | null
|
||||
@@ -51,14 +52,13 @@ export function VacationRescueTip({
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
className="tip-card tip-card--action vacation-rescue-tip"
|
||||
role="article"
|
||||
aria-label={t('tip.aria', { title })}
|
||||
data-testid={`dashboard-vacation-rescue-${index}`}
|
||||
>
|
||||
<div className="tip-card__header">
|
||||
<h4 className="tip-card__title">{title}</h4>
|
||||
<TipCardShell
|
||||
variant="action"
|
||||
title={title}
|
||||
testId={`dashboard-vacation-rescue-${index}`}
|
||||
ariaLabel={t('tip.aria', { title })}
|
||||
className="vacation-rescue-tip"
|
||||
headerExtra={
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn--secondary btn--sm vacation-rescue-tip__dismiss"
|
||||
@@ -69,7 +69,8 @@ export function VacationRescueTip({
|
||||
>
|
||||
{t('planResults.vacationRescueDismiss')}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<p className="tip-card__body">{body}</p>
|
||||
|
||||
<ul className="vacation-rescue-tip__dates">
|
||||
@@ -107,6 +108,6 @@ export function VacationRescueTip({
|
||||
{t('planResults.vacationRescueError')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</TipCardShell>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -215,7 +215,10 @@ export function YearRows({
|
||||
<ul className="year-rows__legend" data-testid="year-rows-legend">
|
||||
{TYPE_ORDER.map((type) => (
|
||||
<li key={type} className="year-rows__legend-item">
|
||||
<span className={`year-rows__swatch year-rows__swatch--${getTypeKey(type)}`} aria-hidden="true" />
|
||||
<span
|
||||
className={`year-rows__swatch year-rows__swatch--${getTypeKey(type)} leave-swatch--${getTypeKey(type)}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{getLabelForType(type, t)}
|
||||
</li>
|
||||
))}
|
||||
@@ -259,7 +262,7 @@ export function YearRows({
|
||||
<button
|
||||
key={`${year}-${idx}`}
|
||||
type="button"
|
||||
className={`year-row__band year-row__band--${getTypeKey(band.dominantType)}`}
|
||||
className={`year-row__band year-row__band--${getTypeKey(band.dominantType)} leave-swatch--${getTypeKey(band.dominantType)}`}
|
||||
style={{ left: `${left}%`, width: `${width}%` }}
|
||||
onClick={(event) => handleBandClick(event, band.startDate)}
|
||||
data-testid={`year-timeline-band-${year}-${idx}`}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { useTranslation, toTranslationParams, type TranslationKey } from '@/i18n'
|
||||
import { TipCardShell } from '@/components/common/TipCardShell'
|
||||
|
||||
export interface TipCardProps {
|
||||
tip: {
|
||||
@@ -79,34 +80,16 @@ export function TipCard({ tip, onDismiss }: TipCardProps): ReactNode {
|
||||
const title = translationKeys ? t(translationKeys.title, params) : tip.messageKey
|
||||
const body = translationKeys ? t(translationKeys.body, params) : tip.messageKey
|
||||
|
||||
const severityClassMap = {
|
||||
info: 'tip-card--info',
|
||||
warning: 'tip-card--warning',
|
||||
action: 'tip-card--action',
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="tip-card"
|
||||
className={`tip-card ${severityClassMap[tip.severity]}`}
|
||||
role="article"
|
||||
aria-label={t('tip.aria', { title })}
|
||||
<TipCardShell
|
||||
variant={tip.severity}
|
||||
title={title}
|
||||
testId="tip-card"
|
||||
ariaLabel={t('tip.aria', { title })}
|
||||
onDismiss={onDismiss ? () => onDismiss(tip.id) : undefined}
|
||||
dismissLabel={t('tip.dismissAria', { title })}
|
||||
>
|
||||
<div className="tip-card__header">
|
||||
<h4 className="tip-card__title">{title}</h4>
|
||||
{onDismiss && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn--icon tip-card__dismiss"
|
||||
onClick={() => onDismiss(tip.id)}
|
||||
aria-label={t('tip.dismissAria', { title })}
|
||||
data-testid="dismiss-button"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="tip-card__body">{body}</p>
|
||||
</div>
|
||||
</TipCardShell>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -416,28 +416,14 @@
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.calendar-band--self::after {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-self);
|
||||
}
|
||||
|
||||
.calendar-band--partner::after {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-partner);
|
||||
}
|
||||
|
||||
/* Lägsta is a white fill told apart by a 1px dark outline and a hatch, not
|
||||
* a hue (see the --c-leave-* tokens); the hatch sits on each lägsta tick so
|
||||
* a mixed-type period still shows which days are which. */
|
||||
.calendar-band--lagsta::after {
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline);
|
||||
}
|
||||
|
||||
.calendar-band--lagsta.calendar-band--self::after {
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline), inset 2px 0 0 var(--c-band-cap-self);
|
||||
}
|
||||
|
||||
.calendar-band--lagsta.calendar-band--partner::after {
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline), inset 2px 0 0 var(--c-band-cap-partner);
|
||||
}
|
||||
/* The self/partner cap-mark and lägsta's outline are the shared
|
||||
* `.leave-swatch--cap-self`/`--cap-partner`/`--lagsta` recipe from
|
||||
* primitives.css, scoped to `::after` here — see the comment there and on
|
||||
* `.calendar-band::after` above for why. `getTypeKey`'s `--lagsta`/
|
||||
* `leave-swatch--lagsta` and the owner's `--self`/`--partner`/
|
||||
* `leave-swatch--cap-*` are added side by side in CalendarWeek.tsx, so
|
||||
* lägsta's outline and the owner's cap keep composing even though this
|
||||
* file no longer states the box-shadow values itself. */
|
||||
|
||||
.calendar-band__day--lagsta {
|
||||
background-image: repeating-linear-gradient(
|
||||
|
||||
@@ -199,27 +199,30 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Fill/hatch/outline is the shared `.leave-swatch--*` recipe from
|
||||
* primitives.css (added alongside these classes in YearRows.tsx); this file
|
||||
* keeps only what's genuinely different at this swatch's small size.
|
||||
*
|
||||
* Vacation's bar uses a distinct, more saturated fill
|
||||
* (--c-leave-vacation-bar) than the calendar band/help-popover sample —
|
||||
* needed for contrast at 14px/8px. Overrides `.leave-swatch--vacation`'s
|
||||
* background-color: same specificity, later in the cascade (components.css
|
||||
* loads after primitives.css). */
|
||||
.year-rows__swatch--vacation,
|
||||
.year-row__band--vacation {
|
||||
background-color: var(--c-leave-vacation-bar);
|
||||
}
|
||||
|
||||
.year-rows__swatch--sjukpenning,
|
||||
.year-row__band--sjukpenning {
|
||||
background-color: var(--c-leave-sjukpenning);
|
||||
}
|
||||
|
||||
/* Lägsta is a white fill told apart by a 1px dark outline and a hatch, not
|
||||
* a hue (see the --c-leave-* tokens). */
|
||||
/* Lägsta's hatch is finer here (3px/4px stops) than the calendar band/
|
||||
* help-popover's 4px/5px, for the same reason. Overrides
|
||||
* `.leave-swatch--lagsta`'s background-image the same way. */
|
||||
.year-rows__swatch--lagsta,
|
||||
.year-row__band--lagsta {
|
||||
background-color: var(--c-leave-lagsta);
|
||||
background-image: repeating-linear-gradient(
|
||||
135deg,
|
||||
transparent 0 3px,
|
||||
var(--c-leave-lagsta-hatch) 3px 4px
|
||||
);
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline);
|
||||
}
|
||||
|
||||
.year-row__summary {
|
||||
|
||||
@@ -587,6 +587,66 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Leave-type swatches
|
||||
* ------------------------------------------------------------------------
|
||||
* Leave types are told apart by fill/hatch/outline, never by hue (see the
|
||||
* `--c-leave-*` tokens in global.css), and the owning parent by a 2px left
|
||||
* cap-mark (`--c-band-cap-*`). This recipe used to be hand-copied across
|
||||
* the calendar band, the year-row swatch/timeline and the calendar-help
|
||||
* legend; consumers keep only their own size/position rule and add one of
|
||||
* these classes alongside their existing BEM modifier.
|
||||
*
|
||||
* `--lagsta`/`--cap-self`/`--cap-partner` are defined twice: once for a
|
||||
* plain element (the year-row swatch, the calendar-help sample — nothing
|
||||
* paints over them) and once scoped to `::after` (the calendar band, whose
|
||||
* per-day ticks sit on top of the band's own background and would hide an
|
||||
* inset shadow drawn directly on it — see `.calendar-band::after` in
|
||||
* calendar.css). The combined lagsta+cap-self / lagsta+cap-partner
|
||||
* `::after` rules exist because `box-shadow` doesn't merge across two
|
||||
* separate rules — the more specific combined selector is what lets a
|
||||
* lägsta band still show its owner's cap. */
|
||||
.leave-swatch--vacation {
|
||||
background-color: var(--c-leave-vacation);
|
||||
}
|
||||
|
||||
.leave-swatch--sjukpenning {
|
||||
background-color: var(--c-leave-sjukpenning);
|
||||
}
|
||||
|
||||
.leave-swatch--lagsta {
|
||||
background-color: var(--c-leave-lagsta);
|
||||
background-image: repeating-linear-gradient(135deg, transparent 0 4px, var(--c-leave-lagsta-hatch) 4px 5px);
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline);
|
||||
}
|
||||
|
||||
.leave-swatch--lagsta::after {
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline);
|
||||
}
|
||||
|
||||
.leave-swatch--cap-self {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-self);
|
||||
}
|
||||
|
||||
.leave-swatch--cap-self::after {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-self);
|
||||
}
|
||||
|
||||
.leave-swatch--cap-partner {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-partner);
|
||||
}
|
||||
|
||||
.leave-swatch--cap-partner::after {
|
||||
box-shadow: inset 2px 0 0 var(--c-band-cap-partner);
|
||||
}
|
||||
|
||||
.leave-swatch--lagsta.leave-swatch--cap-self::after {
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline), inset 2px 0 0 var(--c-band-cap-self);
|
||||
}
|
||||
|
||||
.leave-swatch--lagsta.leave-swatch--cap-partner::after {
|
||||
box-shadow: inset 0 0 0 1px var(--c-leave-lagsta-outline), inset 2px 0 0 var(--c-band-cap-partner);
|
||||
}
|
||||
|
||||
/* Layout helpers
|
||||
* ------------------------------------------------------------------------ */
|
||||
.stack {
|
||||
|
||||
Reference in New Issue
Block a user