Skip to content

Instantly share code, notes, and snippets.

@rafaelcamargo
Last active January 31, 2024 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rafaelcamargo/dfd3f4b8f52534af7bfa039b7f7539a6 to your computer and use it in GitHub Desktop.
Save rafaelcamargo/dfd3f4b8f52534af7bfa039b7f7539a6 to your computer and use it in GitHub Desktop.
In-page scroll link with React
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>In-page scroll link with React</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.13.8/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
<style>
html, body {
margin: 0;
color: #808080;
font-family: 'Playfair Display', Arial, sans-serif;
font-size: 16px;
}
article {
display: flex;
justify-content: space-between;
position: relative;
margin: 0 auto;
padding: 40px 20px;
max-width: 840px;
box-sizing: border-box;
}
menu {
position: sticky;
top: 50px;
left: 0;
margin: 0;
padding: 0;
height: 200px;
}
menu li {
margin: 0;
list-style-type: none;
}
menu li:not(:first-child) {
margin-top: 10px;
}
menu a {
padding: 10px;
width: 100%;
color: #808080;
font-size: 14px;
text-decoration: none;
border-radius: 4px;
box-sizing: border-box;
}
main {
width: 600px;
}
h1 {
margin: 0 0 100px 0;
font-size: 3em;
font-style: italic;
}
h2 {
font-style: italic;
font-weight: 300;
}
section:not(:last-child) {
margin-bottom: 75px;
}
.section-image-placeholder {
height: 400px;
background-color: #f1f1f1;
}
.section-copy-placeholder {
position: relative;
margin: 62px 0;
height: 24px;
background-color: #f1f1f1;
}
.section-copy-placeholder:before,
.section-copy-placeholder:after {
position: absolute;
left: 0;
content: '';
width: 100%;
height: 24px;
background-color: #f1f1f1;
}
.section-copy-placeholder:before {
top: -32px;
}
.section-copy-placeholder:after {
bottom: -32px;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
(function () {
const { createRoot } = ReactDOM;
const ScrollLink = ({ to, children, offset = 0 }) => {
const navigate = evt => {
evt.preventDefault();
window.scroll({
top: getElementTopDistance(to) + window.scrollY - offset,
left: 0,
behavior: 'smooth'
});
}
const getElementTopDistance = elementId => {
const el = document.querySelector(`#${elementId}`);
return el.getBoundingClientRect().top;
}
return (
<a href={`#${to}`} onClick={navigate}>{children}</a>
)
}
const Section = ({ id, heading }) => {
return (
<section id={id}>
<h2>{heading}</h2>
<div class="section-image-placeholder"></div>
<div class="section-copy-placeholder"></div>
</section>
)
}
const App = () => {
const sections = [
{ id: 'intelligence', heading: 'Intelligence' },
{ id: 'conscience', heading: 'Conscience' },
{ id: 'emotion', heading: 'Emotion' },
{ id: 'rebellion', heading: 'Rebellion' }
];
return (
<article>
<menu>
{sections.map(({ id, heading }) => (
<li><ScrollLink to={id} offset={40}>{heading}</ScrollLink></li>
))}
</menu>
<main>
<h1>If I Was The Algorithm</h1>
{sections.map(({ id, heading }) => (
<Section heading={heading} id={id} />
))}
</main>
</article>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<App />);
}())
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment