mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-14 16:51:33 +10:00
fix styling issues and theme cascades across all templates
This commit is contained in:
@ -1,7 +1,6 @@
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import remarkGfm from 'remark-gfm';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children?: string;
|
children?: string;
|
||||||
@ -12,7 +11,7 @@ const Markdown: React.FC<Props> = ({ className, children }) => {
|
|||||||
if (!children || isEmpty(children)) return null;
|
if (!children || isEmpty(children)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]} className={clsx('markdown', className)}>
|
<ReactMarkdown remarkPlugins={[]} className={clsx('markdown', className)}>
|
||||||
{children}
|
{children}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -35,34 +35,55 @@ export const MastheadSidebar: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div className={clsx({ invert: contrast === 'light' })}>
|
||||||
<h1 className="mb-1">{name}</h1>
|
<h1 className="mb-1">{name}</h1>
|
||||||
<p className="opacity-75">{headline}</p>
|
<p className="opacity-75">{headline}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={clsx('flex flex-col gap-2.5', css(`svg { color: ${color} }`))}>
|
<div className={clsx('flex flex-col gap-2.5', css(`svg { color: ${color} }`))}>
|
||||||
<DataDisplay icon={<Room />} className="!gap-2 text-xs">
|
<DataDisplay icon={<Room />} className="!gap-2 text-xs" textClassName={clsx({ invert: contrast === 'light' })}>
|
||||||
{formatLocation(location)}
|
{formatLocation(location)}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Cake />} className="!gap-2 text-xs">
|
<DataDisplay icon={<Cake />} className="!gap-2 text-xs" textClassName={clsx({ invert: contrast === 'light' })}>
|
||||||
{formatDateString(birthdate, dateFormat)}
|
{formatDateString(birthdate, dateFormat)}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Email />} className="!gap-2 text-xs" link={`mailto:${email}`}>
|
<DataDisplay
|
||||||
|
icon={<Email />}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
link={`mailto:${email}`}
|
||||||
|
textClassName={clsx({ invert: contrast === 'light' })}
|
||||||
|
>
|
||||||
{email}
|
{email}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Phone />} className="!gap-2 text-xs" link={`tel:${phone}`}>
|
<DataDisplay
|
||||||
|
icon={<Phone />}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
link={`tel:${phone}`}
|
||||||
|
textClassName={clsx({ invert: contrast === 'light' })}
|
||||||
|
>
|
||||||
{phone}
|
{phone}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Public />} link={website && addHttp(website)} className="!gap-2 text-xs">
|
<DataDisplay
|
||||||
|
icon={<Public />}
|
||||||
|
link={website && addHttp(website)}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
textClassName={clsx({ invert: contrast === 'light' })}
|
||||||
|
>
|
||||||
{website}
|
{website}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
{profiles.map(({ id, username, network, url }) => (
|
{profiles.map(({ id, username, network, url }) => (
|
||||||
<DataDisplay key={id} icon={getProfileIcon(network)} link={url && addHttp(url)} className="!gap-2 text-xs">
|
<DataDisplay
|
||||||
|
key={id}
|
||||||
|
icon={getProfileIcon(network)}
|
||||||
|
link={url && addHttp(url)}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
textClassName={clsx({ invert: contrast === 'light' })}
|
||||||
|
>
|
||||||
{username}
|
{username}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Email, Link, Phone } from '@mui/icons-material';
|
import { Email, Link, Phone } from '@mui/icons-material';
|
||||||
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
import { ListItem, Section as SectionType } from '@reactive-resume/schema';
|
||||||
|
import clsx from 'clsx';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import isArray from 'lodash/isArray';
|
import isArray from 'lodash/isArray';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
@ -23,8 +24,10 @@ const Section: React.FC<SectionProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const section: SectionType = useAppSelector((state) => get(state.resume.present, path, {} as SectionType));
|
const section: SectionType = useAppSelector((state) => get(state.resume.present, path, {} as SectionType));
|
||||||
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
const dateFormat: string = useAppSelector((state) => get(state.resume.present, 'metadata.date.format'));
|
||||||
|
const layout: string[][][] = useAppSelector((state) => get(state.resume.present, 'metadata.layout'));
|
||||||
|
|
||||||
const sectionId = useMemo(() => section.id || path.replace('sections.', ''), [path, section]);
|
const sectionId = useMemo(() => section.id || path.replace('sections.', ''), [path, section]);
|
||||||
|
const isSidebarSection = useMemo(() => layout.some((row) => row[1].includes(sectionId)), [layout, sectionId]);
|
||||||
|
|
||||||
if (!section.visible) return null;
|
if (!section.visible) return null;
|
||||||
|
|
||||||
@ -35,7 +38,7 @@ const Section: React.FC<SectionProps> = ({
|
|||||||
<Heading>{section.name}</Heading>
|
<Heading>{section.name}</Heading>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="grid items-start gap-4"
|
className={clsx('grid items-start gap-4', { invert: isSidebarSection })}
|
||||||
style={{ gridTemplateColumns: `repeat(${section.columns}, minmax(0, 1fr))` }}
|
style={{ gridTemplateColumns: `repeat(${section.columns}, minmax(0, 1fr))` }}
|
||||||
>
|
>
|
||||||
{section.items.map((item: ListItem) => {
|
{section.items.map((item: ListItem) => {
|
||||||
@ -76,8 +79,13 @@ const Section: React.FC<SectionProps> = ({
|
|||||||
key={index}
|
key={index}
|
||||||
className="mr-2 h-3 w-3 rounded-full border"
|
className="mr-2 h-3 w-3 rounded-full border"
|
||||||
style={{
|
style={{
|
||||||
borderColor: 'var(--primary-color)',
|
borderColor: isSidebarSection ? 'var(--text-color)' : 'var(--primary-color)',
|
||||||
backgroundColor: levelNum / (10 / 5) > index ? 'var(--primary-color)' : '',
|
backgroundColor:
|
||||||
|
levelNum / (10 / 5) > index
|
||||||
|
? isSidebarSection
|
||||||
|
? 'var(--text-color)'
|
||||||
|
: 'var(--primary-color)'
|
||||||
|
: '',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@ -94,7 +102,7 @@ const Section: React.FC<SectionProps> = ({
|
|||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{keywords && <div>{keywords.join(', ')}</div>}
|
{keywords && <span>{keywords.join(', ')}</span>}
|
||||||
|
|
||||||
{(phone || email) && (
|
{(phone || email) && (
|
||||||
<div className="grid gap-1">
|
<div className="grid gap-1">
|
||||||
|
|||||||
@ -36,34 +36,55 @@ export const MastheadSidebar: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div className={clsx({ invert: contrast === 'light' })}>
|
||||||
<h1 className="mb-1">{name}</h1>
|
<h1 className="mb-1">{name}</h1>
|
||||||
<p className="opacity-75">{headline}</p>
|
<p className="opacity-75">{headline}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={clsx('flex flex-col gap-2.5', css(`svg { color: ${iconColor} }`))}>
|
<div className={clsx('flex flex-col gap-2.5', css(`svg { color: ${iconColor} }`))}>
|
||||||
<DataDisplay icon={<Room />} className="!gap-2 text-xs">
|
<DataDisplay icon={<Room />} className="!gap-2 text-xs" textClassName={clsx({ invert: contrast === 'light' })}>
|
||||||
{formatLocation(location)}
|
{formatLocation(location)}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Cake />} className="!gap-2 text-xs">
|
<DataDisplay icon={<Cake />} className="!gap-2 text-xs" textClassName={clsx({ invert: contrast === 'light' })}>
|
||||||
{formatDateString(birthdate, dateFormat)}
|
{formatDateString(birthdate, dateFormat)}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Email />} className="!gap-2 text-xs" link={`mailto:${email}`}>
|
<DataDisplay
|
||||||
|
icon={<Email />}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
link={`mailto:${email}`}
|
||||||
|
textClassName={clsx({ invert: contrast === 'light' })}
|
||||||
|
>
|
||||||
{email}
|
{email}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Phone />} className="!gap-2 text-xs" link={`tel:${phone}`}>
|
<DataDisplay
|
||||||
|
icon={<Phone />}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
link={`tel:${phone}`}
|
||||||
|
textClassName={clsx({ invert: contrast === 'light' })}
|
||||||
|
>
|
||||||
{phone}
|
{phone}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
<DataDisplay icon={<Public />} link={website && addHttp(website)} className="!gap-2 text-xs">
|
<DataDisplay
|
||||||
|
icon={<Public />}
|
||||||
|
link={website && addHttp(website)}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
textClassName={clsx({ invert: contrast === 'light' })}
|
||||||
|
>
|
||||||
{website}
|
{website}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
|
|
||||||
{profiles.map(({ id, username, network, url }) => (
|
{profiles.map(({ id, username, network, url }) => (
|
||||||
<DataDisplay key={id} icon={getProfileIcon(network)} link={url && addHttp(url)} className="!gap-2 text-xs">
|
<DataDisplay
|
||||||
|
key={id}
|
||||||
|
icon={getProfileIcon(network)}
|
||||||
|
link={url && addHttp(url)}
|
||||||
|
className="!gap-2 text-xs"
|
||||||
|
textClassName="invert"
|
||||||
|
>
|
||||||
{username}
|
{username}
|
||||||
</DataDisplay>
|
</DataDisplay>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -21,15 +21,8 @@ const BadgeDisplay: React.FC<Props> = ({ items }) => {
|
|||||||
return (
|
return (
|
||||||
<ul className="mt-1 flex flex-wrap gap-2 text-xs">
|
<ul className="mt-1 flex flex-wrap gap-2 text-xs">
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<li
|
<li key={item} className="rounded-sm px-2 py-0.5" style={{ backgroundColor: alpha(theme.primary, 0.75) }}>
|
||||||
key={item}
|
<span style={{ color: contrast === 'dark' ? theme.text : theme.background }}>{item}</span>
|
||||||
className="rounded-sm px-2 py-0.5"
|
|
||||||
style={{
|
|
||||||
color: contrast === 'dark' ? theme.text : theme.background,
|
|
||||||
backgroundColor: alpha(theme.primary, 0.75),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item}
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -20,15 +20,8 @@ const BadgeDisplay: React.FC<Props> = ({ items }) => {
|
|||||||
return (
|
return (
|
||||||
<ul className="my-1 flex flex-wrap items-start justify-center gap-1.5">
|
<ul className="my-1 flex flex-wrap items-start justify-center gap-1.5">
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<li
|
<li key={item} className="rounded-lg px-2 py-0.5 text-xs" style={{ backgroundColor: theme.primary }}>
|
||||||
key={item}
|
<span style={{ color: contrast === 'dark' ? theme.text : theme.background }}>{item}</span>
|
||||||
className="rounded-lg px-2 py-0.5 text-xs"
|
|
||||||
style={{
|
|
||||||
color: contrast === 'dark' ? theme.text : theme.background,
|
|
||||||
backgroundColor: theme.primary,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item}
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
.container {
|
.container {
|
||||||
@apply grid grid-cols-2 gap-4 px-6 py-4;
|
@apply grid grid-cols-2 gap-4 px-6 py-4 items-start;
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
@apply grid gap-4;
|
@apply grid gap-4;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Cake, Email, Phone, Public, Room } from '@mui/icons-material';
|
import { Cake, Email, Phone, Public, Room } from '@mui/icons-material';
|
||||||
import { ThemeConfig } from '@reactive-resume/schema';
|
import { ThemeConfig } from '@reactive-resume/schema';
|
||||||
|
import clsx from 'clsx';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
@ -72,14 +73,14 @@ export const MastheadMain: React.FC = () => {
|
|||||||
className="grid gap-2 p-4"
|
className="grid gap-2 p-4"
|
||||||
style={{ color: contrast === 'dark' ? theme.text : theme.background, backgroundColor: theme.primary }}
|
style={{ color: contrast === 'dark' ? theme.text : theme.background, backgroundColor: theme.primary }}
|
||||||
>
|
>
|
||||||
<div>
|
<div className={clsx({ invert: contrast === 'light' })}>
|
||||||
<h1>{name}</h1>
|
<h1>{name}</h1>
|
||||||
<p className="opacity-75">{headline}</p>
|
<p className="opacity-75">{headline}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr className="opacity-25" />
|
<hr className="opacity-25" />
|
||||||
|
|
||||||
<Markdown>{summary}</Markdown>
|
<Markdown className={clsx({ invert: contrast === 'light' })}>{summary}</Markdown>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,16 +5,17 @@ type Props = {
|
|||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
link?: string;
|
link?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
textClassName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DataDisplay: React.FC<React.PropsWithChildren<Props>> = ({ icon, link, className, children }) => {
|
const DataDisplay: React.FC<React.PropsWithChildren<Props>> = ({ icon, link, className, textClassName, children }) => {
|
||||||
if (isEmpty(children)) return null;
|
if (isEmpty(children)) return null;
|
||||||
|
|
||||||
if (!isEmpty(link)) {
|
if (!isEmpty(link)) {
|
||||||
return (
|
return (
|
||||||
<div className={clsx('inline-flex items-center gap-1', className)}>
|
<div className={clsx('inline-flex items-center gap-1', className)}>
|
||||||
{icon}
|
{icon}
|
||||||
<a href={link} target="_blank" rel="noreferrer">
|
<a href={link} target="_blank" rel="noreferrer" className={textClassName}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -24,7 +25,7 @@ const DataDisplay: React.FC<React.PropsWithChildren<Props>> = ({ icon, link, cla
|
|||||||
return (
|
return (
|
||||||
<div className={clsx('inline-flex items-center gap-1', className)}>
|
<div className={clsx('inline-flex items-center gap-1', className)}>
|
||||||
{icon}
|
{icon}
|
||||||
<span>{children}</span>
|
<span className={textClassName}>{children}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export const generateTypographyStyles = ({ family, size }: Typography): string =
|
|||||||
font-size: ${size.body}px !important;
|
font-size: ${size.body}px !important;
|
||||||
font-family: ${family.body} !important;
|
font-family: ${family.body} !important;
|
||||||
|
|
||||||
svg { font-size: ${size.body}px !important; }
|
p, li, svg { font-size: ${size.body}px !important; line-height: ${size.body * 1.5}px !important; }
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
@ -25,25 +25,31 @@ export const generateTypographyStyles = ({ family, size }: Typography): string =
|
|||||||
h4 { font-size: ${size.heading / 2.5}px !important; line-height: ${size.heading / 2.5}px !important; }
|
h4 { font-size: ${size.heading / 2.5}px !important; line-height: ${size.heading / 2.5}px !important; }
|
||||||
h5 { font-size: ${size.heading / 3}px !important; line-height: ${size.heading / 3}px !important; }
|
h5 { font-size: ${size.heading / 3}px !important; line-height: ${size.heading / 3}px !important; }
|
||||||
h6 { font-size: ${size.heading / 3.5}px !important; line-height: ${size.heading / 3.5}px !important; }
|
h6 { font-size: ${size.heading / 3.5}px !important; line-height: ${size.heading / 3.5}px !important; }
|
||||||
|
|
||||||
.markdown p,
|
|
||||||
.markdown li {
|
|
||||||
font-size: ${size.body}px !important;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const generateThemeStyles = ({ text, background, primary }: ThemeConfig): string => `
|
export const generateThemeStyles = ({ text, background, primary }: ThemeConfig): string => `
|
||||||
color: ${text} !important;
|
--text-color: ${text} !important;
|
||||||
background-color: ${background} !important;
|
|
||||||
--primary-color: ${primary} !important;
|
--primary-color: ${primary} !important;
|
||||||
|
--background-color: ${background} !important;
|
||||||
|
|
||||||
svg {
|
color: var(--text-color);
|
||||||
color: var(--primary-color) !important;
|
background-color: var(--background-color);
|
||||||
|
|
||||||
|
span,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
li,
|
||||||
|
p,
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown p,
|
svg {
|
||||||
.markdown li {
|
color: var(--primary-color);
|
||||||
color: ${text} !important;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user