Skip to content

Commit

Permalink
chore(ui): homepage initial setup
Browse files Browse the repository at this point in the history
  • Loading branch information
csantiago132 committed Dec 10, 2024
1 parent 5048542 commit 787d98b
Show file tree
Hide file tree
Showing 10 changed files with 573 additions and 42 deletions.
92 changes: 92 additions & 0 deletions app/lib/HorizontalScrollText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { wrap } from '@motionone/utils';
import clsx from 'clsx';
import type { MotionValue } from 'framer-motion';
import {
motion,
useAnimationFrame,
useMotionValue,
useScroll,
useSpring,
useTransform,
useVelocity,
} from 'framer-motion';
import React, { useRef } from 'react';

import type { FramerCursorAttributes } from '~/types';

interface HorizontalScrollTextProps extends FramerCursorAttributes {
children: string;
baseVelocity: number;
className?: string;
href?: string;
}

export function HorizontalScrollText({
children,
baseVelocity = 100,
className,
href,
onMouseEnter,
onMouseLeave,
}: HorizontalScrollTextProps): React.ReactNode {
const baseX = useMotionValue(0);

const { scrollY } = useScroll();

const scrollVelocity = useVelocity(scrollY);

const smoothVelocity = useSpring(scrollVelocity, {
damping: 50,
stiffness: 400,
}) as MotionValue<number>;

const velocityFactor = useTransform(smoothVelocity, [0, 1000], [0, 5], {
clamp: false,
});

const motionValuePayload = useTransform(
baseX,

(baseY) => `${String(wrap(-20, -45, baseY))}%`,
);

const directionFactor = useRef<number>(1);

useAnimationFrame((_, delta) => {
let moveBy = directionFactor.current * baseVelocity * (delta / 1000);
directionFactor.current = velocityFactor.get() < 0 ? -1 : 1;
moveBy += directionFactor.current * moveBy * velocityFactor.get();

baseX.set(baseX.get() + moveBy);
});

const HtmlTag = typeof href === 'string' ? motion.a : motion.h1;

return (
<div className='relative flex overflow-hidden whitespace-nowrap'>
<motion.div
className='relative flex whitespace-nowrap font-semibold uppercase'
style={{ x: motionValuePayload }}
>
{Array.from({ length: 12 }, (_, idx) => (
<HtmlTag
{...(href && { href })}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
key={idx}
className={clsx(
'block font-display text-6xl font-semibold [text-wrap:balance] md:text-7xl lg:text-8xl',
className,
)}
style={{
lineHeight: '1.0',
color: idx % 3 === 0 ? '#344054' : '#e0e6e6',
}}
>
{children}
</HtmlTag>
))}
</motion.div>
</div>
);
}
2 changes: 1 addition & 1 deletion app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function Layout({
href='https://cdn.jsdelivr.net/gh/devicons/devicon@latest/devicon.min.css'
/>
<Links />
<title>Welcome to Remix</title>
<title>Kurocado Studio</title>
</head>
<body
className='selection:bg-lime-200 selection:text-[#f52891cc]'
Expand Down
54 changes: 16 additions & 38 deletions app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,26 @@
import type { MetaFunction } from '@remix-run/node';
import React from 'react';

import { Intro } from '~/views/Intro';

export const meta: MetaFunction = () => {
return [
{ title: 'New Remix App' },
{ name: 'description', content: 'Welcome to Remix!' },
{ title: 'Kurocado Studio' },
{
name: 'description',
content:
'Kurocado Studio specializes in SaaS development, open-source projects, and personalized web solutions.',
},
{
name: 'keywords',
content:
'Kurocado Studio, SaaS, open-source, web development, TypeScript',
},
{ name: 'author', content: 'Carlos Santiago' },
{ property: 'og:title', content: 'Kurocado Studio' },
];
};

export default function Index(): React.ReactNode {
return (
<div
style={{
fontFamily: 'system-ui, sans-serif',
lineHeight: '1.8',
}}
className='bg-blue-500 text-white'
>
<h1>Welcome to Remix</h1>
<ul>
<li>
<a
target='_blank'
href='https://remix.run/tutorials/blog'
rel='noreferrer'
>
15m Quickstart Blog Tutorial
</a>
</li>
<li>
<a
target='_blank'
href='https://remix.run/tutorials/jokes'
rel='noreferrer'
>
Deep Dive Jokes App Tutorial
</a>
</li>
<li>
<a target='_blank' href='https://remix.run/docs' rel='noreferrer'>
Remix Docs
</a>
</li>
</ul>
</div>
);
return <Intro />;
}
2 changes: 1 addition & 1 deletion app/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*
* Explore our open-source projects: {@link https://github.com/kurocado-studio}
*/

@import './typography.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
6 changes: 6 additions & 0 deletions app/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react';

Check warning on line 1 in app/types/index.ts

View workflow job for this annotation

GitHub Actions / lint / lint

All imports in the declaration are only used as types. Use `import type`

export type FramerCursorAttributes = Pick<
Partial<React.DOMAttributes<HTMLElement>>,
'onMouseEnter' | 'onMouseLeave'
>;
205 changes: 205 additions & 0 deletions app/typography.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
@layer components {
.typography {
color: theme(colors.neutral.950);
font-size: theme(fontSize.xl);
line-height: theme(fontSize.xl[1].lineHeight);
--shiki-color-background: theme(colors.neutral.950);
--shiki-color-text: theme(colors.white);
--shiki-token-comment: theme(colors.neutral.500);
--shiki-token-constant: theme(colors.neutral.300);
--shiki-token-function: theme(colors.neutral.300);
--shiki-token-keyword: theme(colors.neutral.400);
--shiki-token-parameter: theme(colors.neutral.400);
--shiki-token-punctuation: theme(colors.neutral.400);
--shiki-token-string-expression: theme(colors.neutral.300);
--shiki-token-string: theme(colors.neutral.400);

:where(.typography > *) {
margin-top: theme(spacing.6);
margin-bottom: theme(spacing.6);
}

/* Headings */

:where(h1) {
font-family: theme(fontFamily.display);
font-size: theme(fontSize.4xl);
font-variation-settings: theme(
fontFamily.display[1].fontVariationSettings
);
font-weight: theme(fontWeight.semibold);
line-height: theme(fontSize.4xl[1].lineHeight);
margin-top: theme(spacing.16);
}

:where(h2) {
font-family: theme(fontFamily.display);
font-size: theme(fontSize.2xl);
font-variation-settings: theme(
fontFamily.display[1].fontVariationSettings
);
font-weight: theme(fontWeight.semibold);
line-height: theme(fontSize.2xl[1].lineHeight);
margin-top: theme(spacing.16);
}

:where(h3) {
font-family: theme(fontFamily.display);
font-size: theme(fontSize.xl);
font-variation-settings: theme(
fontFamily.display[1].fontVariationSettings
);
font-weight: theme(fontWeight.semibold);
line-height: theme(fontSize.xl[1].lineHeight);
margin-top: theme(spacing.10);
}

:where(h2 + h3) {
margin-top: 0;
}

/* Lists */

:where(ul, ol) {
padding-left: 1.5rem;
}

:where(ul) {
list-style-type: disc;
}

:where(ol) {
list-style-type: decimal;
}

:where(li) {
padding-left: theme(spacing.3);
margin-top: theme(spacing.6);
}

:where(li)::marker {
color: theme(colors.neutral.500);
}

:where(li > *),
:where(li li) {
margin-top: theme(spacing.4);
}

:where(ol > li)::marker {
font-size: theme(fontSize.base);
font-weight: theme(fontWeight.semibold);
}

/* Tables */

:where(table) {
font-size: theme(fontSize.base);
line-height: theme(fontSize.base[1].lineHeight);
text-align: left;
width: 100%;
}

:where(th) {
font-weight: theme(fontWeight.semibold);
}

:where(thead th) {
border-bottom: 1px solid theme(colors.neutral.950);
padding-bottom: theme(spacing.6);
}

:where(td) {
border-bottom: 1px solid theme(colors.neutral.950 / 0.1);
padding-bottom: theme(spacing.6);
padding-top: theme(spacing.6);
vertical-align: top;
}

:where(:is(th, td):not(:last-child)) {
padding-right: theme(spacing.6);
}

/* Code blocks */

:where(pre) {
background-color: theme(colors.neutral.950);
border-radius: theme(borderRadius.4xl);
display: flex;
margin: theme(spacing.10) calc(-1 * theme(spacing.6));
overflow-x: auto;

@screen sm {
margin-left: auto;
margin-right: auto;
}
}

:where(pre code) {
color: theme(colors.white);
flex: none;
font-size: theme(fontSize.base);
line-height: theme(lineHeight.8);
padding: theme(padding.8) theme(padding.6);

@screen sm {
padding: theme(spacing.10);
}
}

/* <hr> */

:where(hr) {
border-color: theme(colors.neutral.950 / 0.1);
margin-bottom: theme(spacing.24);
margin-top: theme(spacing.24);
}

/* Inline text */

:where(a) {
font-weight: theme(fontWeight.semibold);
text-decoration-skip-ink: none;
text-decoration-thickness: 1px;
text-decoration: underline;
text-underline-offset: 0.15em;
}

:where(strong) {
font-weight: theme(fontWeight.semibold);
}

:where(code:not(pre code)) {
font-size: calc(18 / 20 * 1em);
font-weight: theme(fontWeight.semibold);

&::before,
&::after {
content: '`';
}
}

:where(h2 code, h3 code) {
font-weight: theme(fontWeight.bold);
}

/* Figures */

:where(figure) {
margin-top: theme(spacing.32);
margin-bottom: theme(spacing.32);
}

/* Spacing overrides */

:where(.typography:first-child > :first-child),
:where(li > :first-child) {
margin-top: 0 !important;
}

:where(.typography:last-child > :last-child),
:where(li > :last-child) {
margin-bottom: 0 !important;
}
}
}
Loading

0 comments on commit 787d98b

Please sign in to comment.