Skip to content

Instantly share code, notes, and snippets.

@ben-rogerson
Last active August 20, 2024 09:19
Show Gist options
  • Save ben-rogerson/b4b406dffcc18ae02f8a6c8c97bb58a8 to your computer and use it in GitHub Desktop.
Save ben-rogerson/b4b406dffcc18ae02f8a6c8c97bb58a8 to your computer and use it in GitHub Desktop.
A breakpoint provider that syncs up with your screens in tailwind.config.js. Common use is to fully remove elements from the dom rather than hide them with css.
import React from 'react';
import { useMinScreen } from './minScreen';
const App = () => {
const { min } = useMinScreen();
return (
<>
{min`md` && <div>I'll show at md and up</div>}
{!min`lg` && <div>I'll show at up to lg</div>}
{min`sm` && !min`xl` && <div>I'll show between sm and xl</div>}
</>
);
};
export default App;
import { render } from 'react-dom';
import { theme } from 'twin.macro';
import App from './App';
import { MinScreenProvider } from './minScreen';
import { theme } from 'twin.macro';
render(
<MinScreenProvider screens={theme`screens`}>
<App />
</MinScreenProvider>,
document.getElementById('root'),
);
import React, { useState, useEffect, createContext, useContext } from 'react';
/**
* Screen provider and hook for Twin
*/
const defaultValue = {};
const ScreensContext = createContext(defaultValue);
const MinScreenProvider = ({ children, screens }) => {
const [queryMatch, setQueryMatch] = useState({});
useEffect(() => {
const mediaQueryLists = {};
let isAttached = false;
const mediaData = Object.entries(screens).map(([name, media]) => [
name,
`(min-width: ${media})`
]);
const handleQueryListener = () => {
const updatedMatches = mediaData.reduce(
(acc, [name]) => ({
...acc,
[name]: Boolean(
mediaQueryLists[name] && mediaQueryLists[name].matches
)
}),
{}
);
setQueryMatch(updatedMatches);
};
if (window && window.matchMedia) {
const matches = {};
mediaData.forEach(([name, media]) => {
if (typeof media !== 'string') {
matches[name] = false;
return;
}
mediaQueryLists[name] = window.matchMedia(media);
matches[name] = mediaQueryLists[name].matches;
});
setQueryMatch(matches);
isAttached = true;
mediaData.forEach(([name, media]) => {
if (typeof media !== 'string') return;
mediaQueryLists[name].addListener(handleQueryListener);
});
}
return () => {
if (!isAttached) return;
mediaData.forEach(([name, media]) => {
if (typeof media !== 'string') return;
mediaQueryLists[name].removeListener(handleQueryListener);
});
};
}, [screens]);
return (
<ScreensContext.Provider value={queryMatch}>
{children}
</ScreensContext.Provider>
);
};
const useMinScreen = () => {
const context = useContext(ScreensContext);
if (context === defaultValue)
throw new Error('useMinScreen must be used within a MinScreenProvider');
return { min: (size) => context[size] };
};
export { useMinScreen, MinScreenProvider };
@DavithkbY
Copy link

Any typescript solution examples for this?

@ben-rogerson
Copy link
Author

Check out mezz - particularly syncing with tailwind

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment