Cannot specify media query in different module - Invalid pseudo or at-rule.
Describe the issue
Im getting an error when I try to specify a helper for media queries to not have to write the string in every condition (or a const in every module I want to use it)
Invalid pseudo or at-rule.
If I specify the const in the same module, it works.
It doesn't matter if it is in a .stylex.ts file or .ts only.
// media.stylex.ts or media.ts
export const MEDIA = {
md: "@media (min-width: 1024px)",
lg: "@media (min-width: 1920px)",
};
//Component.tsx
const styles = css.create({
comp: {
width: {
default: 100,
[MEDIA.md]: 200
}
}
})
export const Comp = () => {
return <html.div style={styles.comp} />
}
Expected behavior
Expect to be able to import media query helper from a different module.
Steps to reproduce
Basic setup.
- Create a basic RSD install
- Create a media.stylex.ts file
- Add this block to it
export const MEDIA = {
md: "@media (min-width: 1024px)",
lg: "@media (min-width: 1920px)",
};
- Create a Comp.tsx file
- Add a component using this media query and import the MEDIA const from the other module
const styles = css.create({
comp: {
width: {
default: 100,
[MEDIA.md]: 200
}
}
})
export const Comp = () => {
return <html.div style={styles.comp}>Hello</html.div>
}
Test case
No response
Additional comments
strict-dom version 0.0.27
This is a shortcoming of StyleX. See https://github.com/facebook/stylex/issues/724
Any workarounds or suggestions for code splitting?
@LunatiqueCoder The easiest workaround is to repeat the media queries in every file where you need to use them and use Typescript types to enforce that they remain the same across files.
export type TSM = '@media (max-width: 768px)';
/// other file
const SM: TSM = '@media (max-width: 768px)';
What do you mean when saying "code splitting"?
@nmn oh your solution should work pretty good. Thank you for the suggestion!
Here's my take:
1. Declare your breakpoints like this:
breakpoints.stylex.ts:
export const BREAKPOINTS = {
xl: 1650,
lg: 1280,
md: 1020,
sm: 800,
xs: 660,
xxs: 390,
gtXl: 1651,
gtLg: 1281,
gtMd: 1021,
gtSm: 801,
gtXs: 661,
gtXxs: 391,
} as const;
export const MEDIA_QUERIES = {
// Max-width queries
xl: `(max-width: ${BREAKPOINTS.xl}px)`,
lg: `(max-width: ${BREAKPOINTS.lg}px)`,
md: `(max-width: ${BREAKPOINTS.md}px)`,
sm: `(max-width: ${BREAKPOINTS.sm}px)`,
xs: `(max-width: ${BREAKPOINTS.xs}px)`,
xxs: `(max-width: ${BREAKPOINTS.xxs}px)`,
// Min-width queries (GT = greater than)
gtXl: `(min-width: ${BREAKPOINTS.gtXl}px)`,
gtLg: `(min-width: ${BREAKPOINTS.gtLg}px)`,
gtMd: `(min-width: ${BREAKPOINTS.gtMd}px)`,
gtSm: `(min-width: ${BREAKPOINTS.gtSm}px)`,
gtXs: `(min-width: ${BREAKPOINTS.gtXs}px)`,
gtXxs: `(min-width: ${BREAKPOINTS.gtXxs}px)`,
// Other media queries
short: '(max-height: 820px)',
tall: '(min-height: 820px)',
hoverNone: '(hover: none)',
pointerCoarse: '(pointer: coarse)',
pointerFine: '(pointer: fine)',
} as const;
// Helper to pick specific media queries when needed
export type TPickMediaQuery<TPick extends keyof typeof MEDIA_QUERIES> = {
[key in TPick]: `@media ${(typeof MEDIA_QUERIES)[key]}`;
};
2. Then you can use it like this:
import { TPickMediaQuery } from '@/src/theme/breakpoints.stylex';
const MEDIA_QUERY: TPickMediaQuery<'xl' | 'xs'> = { // Pick your needed breakpoints here. TS will help you autocomplete the values
xl: '@media (max-width: 1650px)',
xs: '@media (max-width: 660px)',
};
export const styles = css.create({
headerImage: {
height: {
default: '300px',
[MEDIA_QUERY.xs]: '203px',
},
},
image: {
width: {
default: '525px',
[MEDIA_QUERY.xs]: '273px',
},
height: {
default: '391px',
[MEDIA_QUERY.xs]: '203px',
},
},
}
3. BONUS: Use the breakpoints with @expo/match-media and react-responsive:
useMedia.ts:
import { useMediaQuery } from 'react-responsive';
import { MEDIA_QUERIES } from '../theme/breakpoints.stylex';
export const useMedia = () => {
const xl = useMediaQuery({ query: MEDIA_QUERIES.xl });
const lg = useMediaQuery({ query: MEDIA_QUERIES.lg });
const md = useMediaQuery({ query: MEDIA_QUERIES.md });
const sm = useMediaQuery({ query: MEDIA_QUERIES.sm });
const xs = useMediaQuery({ query: MEDIA_QUERIES.xs });
const xxs = useMediaQuery({ query: MEDIA_QUERIES.xxs });
const gtXs = useMediaQuery({ query: MEDIA_QUERIES.gtXs });
const gtSm = useMediaQuery({ query: MEDIA_QUERIES.gtSm });
const gtMd = useMediaQuery({ query: MEDIA_QUERIES.gtMd });
const gtLg = useMediaQuery({ query: MEDIA_QUERIES.gtLg });
const gtXl = useMediaQuery({ query: MEDIA_QUERIES.gtXl });
const short = useMediaQuery({ query: MEDIA_QUERIES.short });
const tall = useMediaQuery({ query: MEDIA_QUERIES.tall });
const hoverNone = useMediaQuery({ query: MEDIA_QUERIES.hoverNone });
const pointerCoarse = useMediaQuery({ query: MEDIA_QUERIES.pointerCoarse });
const pointerFine = useMediaQuery({ query: MEDIA_QUERIES.pointerFine });
return {
xl,
lg,
md,
sm,
xs,
xxs,
gtXs,
gtSm,
gtMd,
gtLg,
gtXl,
short,
tall,
hoverNone,
pointerCoarse,
pointerFine,
};
};
I would generally recommend against using useMatchMedia on the web as it breaks SSR. Outside of that, cool trick with Typescript.
We're working on a feature that'll make it possible to actually share media queries soon.