initial commit and first version release

This commit is contained in:
Refansa 2023-10-16 10:39:32 +07:00
parent b3603c51cf
commit 10b179f6e3
40 changed files with 3486 additions and 42 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
APP_NAME=portfolio
APP_LANG=en_US

2
.env.example Normal file
View File

@ -0,0 +1,2 @@
APP_NAME=
APP_LANG=

View File

@ -1,3 +1,7 @@
{
"extends": "next/core-web-vitals"
"extends": ["next/core-web-vitals", "prettier"],
"rules": {
"react/no-unescaped-entities": "off",
"@next/next/no-page-custom-font": "off"
}
}

53
.prettierignore Normal file
View File

@ -0,0 +1,53 @@
# Using this project's .gitignore file as base.
#
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next
/out
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# configs
package.json
package-lock.json
next.config.js
pnpm-lock.yaml
postcss.config.js
tsconfig.json
yarn.lock
.eslintrc.json
.prettierrc.json
# public
/public
README.md

14
.prettierrc.json Normal file
View File

@ -0,0 +1,14 @@
{
"printWidth": 80,
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"quoteProps": "as-needed",
"jsxSingleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"bracketSameLine": true,
"arrowParens": "always",
"endOfLine": "lf",
"singleAttributePerLine": true
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"discord.enabled": false
}

View File

@ -1,27 +0,0 @@
import "@mantine/core/styles.css";
import React from "react";
import { MantineProvider, ColorSchemeScript } from "@mantine/core";
import { theme } from "../theme";
export const metadata = {
title: "Mantine Next.js template",
description: "I am using Mantine with Next.js!",
};
export default function RootLayout({ children }: { children: any }) {
return (
<html lang="en">
<head>
<ColorSchemeScript />
<link rel="shortcut icon" href="/favicon.svg" />
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, user-scalable=no"
/>
</head>
<body>
<MantineProvider theme={theme}>{children}</MantineProvider>
</body>
</html>
);
}

View File

@ -1,3 +0,0 @@
export default function HomePage() {
return <div>Home page</div>;
}

View File

@ -1,19 +1,28 @@
{
"name": "mantine-minimal-next-template",
"name": "portfolio",
"version": "0.1.0",
"private": true,
"author": {
"name": "Refansa",
"email": "m.refansa.am@gmail.com",
"url": "https://github.com/Refansa"
},
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"prettier": "prettier . \"./**/*.{js,jsx,ts,tsx}\" --write"
},
"dependencies": {
"@mantine/core": "7.1.3",
"@mantine/hooks": "7.1.3",
"@tabler/icons-react": "^2.39.0",
"framer-motion": "^10.16.4",
"next": "13.4.4",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"react-intersection-observer": "^9.5.2"
},
"devDependencies": {
"@types/node": "20.2.5",
@ -21,9 +30,11 @@
"@types/react-dom": "18.2.4",
"eslint": "8.41.0",
"eslint-config-next": "13.4.4",
"eslint-config-prettier": "^9.0.0",
"postcss": "^8.4.24",
"postcss-preset-mantine": "1.8.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "3.0.3",
"typescript": "5.0.4"
}
}

2656
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 56 KiB

4
public/grid.svg Normal file
View File

@ -0,0 +1,4 @@
<svg id="download" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
<path id="Path_3" data-name="Path 3" d="M96,95h4v1H96v4H95V96H86v4H85V96H76v4H75V96H66v4H65V96H56v4H55V96H46v4H45V96H36v4H35V96H26v4H25V96H16v4H15V96H0V95H15V86H0V85H15V76H0V75H15V66H0V65H15V56H0V55H15V46H0V45H15V36H0V35H15V26H0V25H15V16H0V15H15V0h1V15h9V0h1V15h9V0h1V15h9V0h1V15h9V0h1V15h9V0h1V15h9V0h1V15h9V0h1V15h9V0h1V15h4v1H96v9h4v1H96v9h4v1H96v9h4v1H96v9h4v1H96v9h4v1H96v9h4v1H96v9h4v1H96Zm-1,0V86H86v9ZM85,95V86H76v9ZM75,95V86H66v9ZM65,95V86H56v9ZM55,95V86H46v9ZM45,95V86H36v9ZM35,95V86H26v9ZM25,95V86H16v9ZM16,85h9V76H16Zm10,0h9V76H26Zm10,0h9V76H36Zm10,0h9V76H46Zm10,0h9V76H56Zm10,0h9V76H66Zm10,0h9V76H76Zm10,0h9V76H86Zm9-10V66H86v9ZM85,75V66H76v9ZM75,75V66H66v9ZM65,75V66H56v9ZM55,75V66H46v9ZM45,75V66H36v9ZM35,75V66H26v9ZM25,75V66H16v9ZM16,65h9V56H16Zm10,0h9V56H26Zm10,0h9V56H36Zm10,0h9V56H46Zm10,0h9V56H56Zm10,0h9V56H66Zm10,0h9V56H76Zm10,0h9V56H86Zm9-10V46H86v9ZM85,55V46H76v9ZM75,55V46H66v9ZM65,55V46H56v9ZM55,55V46H46v9ZM45,55V46H36v9ZM35,55V46H26v9ZM25,55V46H16v9ZM16,45h9V36H16Zm10,0h9V36H26Zm10,0h9V36H36Zm10,0h9V36H46Zm10,0h9V36H56Zm10,0h9V36H66Zm10,0h9V36H76Zm10,0h9V36H86Zm9-10V26H86v9ZM85,35V26H76v9ZM75,35V26H66v9ZM65,35V26H56v9ZM55,35V26H46v9ZM45,35V26H36v9ZM35,35V26H26v9ZM25,35V26H16v9ZM16,25h9V16H16Zm10,0h9V16H26Zm10,0h9V16H36Zm10,0h9V16H46Zm10,0h9V16H56Zm10,0h9V16H66Zm10,0h9V16H76Zm10,0h9V16H86Z" fill="rgba(255,255,255,0.1)" fill-rule="evenodd" opacity="0.5"/>
<path id="Path_4" data-name="Path 4" d="M6,5V0H5V5H0V6H5v94H6V6h94V5Z" fill="rgba(255,255,255,0.1)" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

12
src/app/global.css Normal file
View File

@ -0,0 +1,12 @@
html {
scroll-behavior: smooth;
}
/* Offset the anchor hashtag link
*/
:target::before {
content: '';
display: block;
height: 144px;
margin-top: -144px;
}

67
src/app/layout.tsx Normal file
View File

@ -0,0 +1,67 @@
import { MantineProvider, ColorSchemeScript } from '@mantine/core'
import { Metadata } from 'next'
import './global.css'
import '@mantine/core/styles.css'
import { resolver, theme } from '../styles/theme'
export const metadata: Metadata = {
authors: [
{
name: 'Refansa',
url: 'https://github.com/Refansa',
},
],
title: 'Refansa - Software Developer',
description: "Refansa's portfolio website",
viewport: 'width=device-width, initial-scale=1',
colorScheme: 'dark',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang={process.env.APP_LANG ?? 'en'}>
<head>
<link
rel='preconnect'
href='https://fonts.googleapis.com'
crossOrigin='anonymous'
/>
<link
rel='preconnect'
href='https://fonts.gstatic.com'
crossOrigin='anonymous'
/>
<link
href='https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&display=swap'
rel='preload'
/>
<link
href='https://fonts.googleapis.com/css2?family=Gabarito:wght@400;500;600;700;800;900&display=swap'
rel='preload'
/>
<link
href='https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'
rel='preload'
/>
<ColorSchemeScript
defaultColorScheme={'dark'}
forceColorScheme={'dark'}
/>
</head>
<body>
<MantineProvider
defaultColorScheme={'dark'}
forceColorScheme={'dark'}
cssVariablesResolver={resolver}
theme={theme}>
{children}
</MantineProvider>
</body>
</html>
)
}

33
src/app/page.tsx Normal file
View File

@ -0,0 +1,33 @@
'use client'
import { AppShell, Container, rem } from '@mantine/core'
import Introduction from '../partials/Introduction'
import About from '../partials/About'
import Navbar from '../components/Navbar'
import Footer from '../components/Footer'
import SlideUpWhenVisible from '../hooks/slideUpWhenVisible'
export default function HomePage() {
return (
<AppShell
withBorder={false}
padding={'xl'}
header={{ height: 72 }}
className={'shell-root'}>
<AppShell.Header>
<Navbar />
</AppShell.Header>
<AppShell.Main>
<SlideUpWhenVisible>
<Container
size={rem(1920)}
px={{ base: '0rem', lg: '4rem', xl: '8rem' }}>
<Introduction />
<About />
<Footer />
</Container>
</SlideUpWhenVisible>
</AppShell.Main>
</AppShell>
)
}

18
src/components/Footer.tsx Normal file
View File

@ -0,0 +1,18 @@
import { Anchor, Container, Text } from '@mantine/core'
export default function Footer() {
return (
<Container>
<Text
fw='bold'
ta='center'>
Created with by{' '}
<Anchor
fw='bold'
href='https://github.com/Refansa'>
Refansa
</Anchor>
</Text>
</Container>
)
}

View File

@ -0,0 +1,3 @@
.bracket {
color: var(--mantine-color-dark-4);
}

View File

@ -0,0 +1,21 @@
import { Anchor, Title } from '@mantine/core'
import Link from 'next/link'
import classes from './NavIcon.module.css'
function NavIcon() {
return (
<Anchor
component={Link}
href='/'
underline='never'>
<Title ff='Fira Code'>
<span className={classes.bracket}>{'{'}</span>
<span>R</span>
<span className={classes.bracket}>{'}'}</span>
</Title>
</Anchor>
)
}
export default NavIcon

View File

@ -0,0 +1,20 @@
import { Button } from '@mantine/core'
interface NavLinkProps {
content: string
href: string
}
function NavLink({ content, href }: NavLinkProps) {
return (
<Button
component={'a'}
variant='subtle'
size='md'
href={href}>
{content}
</Button>
)
}
export default NavLink

View File

@ -0,0 +1,8 @@
.navigation {
display: flex;
padding-inline: 3vw;
align-items: center;
justify-content: space-between;
height: 100%;
border-bottom: 1px solid var(--mantine-color-dark-7);
}

27
src/components/Navbar.tsx Normal file
View File

@ -0,0 +1,27 @@
import { Box, Group } from '@mantine/core'
import classes from './Navbar.module.css'
import NavIcon from './NavIcon'
import NavLink from './NavLink'
function Navbar() {
return (
<Box
component='nav'
className={classes.navigation}>
<NavIcon />
<Group visibleFrom='sm'>
<NavLink
content='About'
href='/#about'
/>
<NavLink
content='Projects'
href='https://github.com/Refansa?tab=repositories'
/>
</Group>
</Box>
)
}
export default Navbar

View File

@ -0,0 +1,33 @@
import { motion, useAnimation } from 'framer-motion'
import { useEffect } from 'react'
import { useInView } from 'react-intersection-observer'
export default function SlideUpWhenVisible({
children,
threshold,
}: {
children: React.ReactNode
threshold?: number | number[]
}) {
const controls = useAnimation()
const [ref, inView] = useInView({ threshold: threshold ? threshold : 0.35 })
useEffect(() => {
if (inView) {
controls.start('visible')
}
}, [controls, inView])
return (
<motion.div
ref={ref}
animate={controls}
initial='hidden'
transition={{ duration: 0.4 }}
variants={{
visible: { opacity: 1, y: 0 },
hidden: { opacity: 0, y: 20 },
}}>
{children}
</motion.div>
)
}

View File

@ -0,0 +1,3 @@
.grayscale {
filter: grayscale(1);
}

76
src/partials/About.tsx Normal file
View File

@ -0,0 +1,76 @@
import {
Anchor,
Box,
Container,
Image,
SimpleGrid,
Stack,
Text,
Title,
} from '@mantine/core'
import classes from './About.module.css'
function About() {
return (
<Box
id='about'
mt='xl'
mah='100vh'
h='100vh'>
<SimpleGrid
cols={{ base: 1, sm: 2 }}
spacing={{ base: 'sm', md: 'md', lg: 'lg' }}>
<Stack>
<Title>A bit About Me</Title>
<Stack gap='md'>
<Text>
Hello 👋 Refansa here. Muhammad Refansa Ali Muzky is my full name.
I'm an 18 years old software developer, just came fresh out of the
oven. Constantly seeking new open source projects to contribute to
and enjoy working with others to make a meaningful contribution.
</Text>
<Text>
I thrive on challenging coding projects and love nothing more than
diving into complex software development tasks. As a lover of open
source, I believe that sharing knowledge and collaborating on
projects is essential for the advancement of the tech industry.
</Text>
<Text
mt='lg'
c='dark.1'
fs='italic'>
I use Archlinux btw.
</Text>
<Text
mt='xl'
fw='bold'>
Peace from Indonesia 🇮🇩
</Text>
</Stack>
</Stack>
<Container visibleFrom='sm'>
<Stack
justify='center'
w={360}>
<Image
className={classes.grayscale}
src='/assets/hand-coding.svg'
alt='Coding Illustration'
width={360}
height='auto'
/>
<Anchor
ta='center'
fs='italic'
fz='xs'
href='https://storyset.com/work'>
Work illustrations by Storyset
</Anchor>
</Stack>
</Container>
</SimpleGrid>
</Box>
)
}
export default About

View File

@ -0,0 +1,20 @@
.background {
&::before {
content: '';
background-image: url('/grid.svg');
background-repeat: repeat;
position: absolute;
inset: 0;
opacity: 0.4;
width: 100%;
height: 100%;
pointer-events: none;
}
}
.line {
width: 4rem;
height: 0.125rem;
background-color: var(--mantine-color-coffee-8);
border-radius: 1rem;
}

View File

@ -0,0 +1,66 @@
import { Box, Button, Flex, Group, Text, Title } from '@mantine/core'
import { IconBrandGithub, IconMail } from '@tabler/icons-react'
import classes from './Introduction.module.css'
function Introduction() {
return (
<Box
mt='xl'
mah='100vh'
h='80vh'
className={classes.background}>
<Flex
align='center'
gap='md'>
<Box className={classes.line} />
<Text
fz='display-lg'
fw='bold'
c='coffee.6'>
Hi There!
</Text>
</Flex>
<Title
variant='shadow'
fz='display-xl'
fw='bold'
lh={1.2}>
I'm Refansa.
</Title>
<Text
fz='display-lg'
fw='bold'
lh='xs'>
A Passionate, self-taught Software Developer{' '}
<Text
component='span'
display='block'
inherit
c='dark.1'>
A supporter of Open Source Software.
</Text>
</Text>
<Group mt={'xl'}>
<Button
component='a'
href='https://github.com/Refansa'
target='_blank'
leftSection={<IconBrandGithub />}
size='lg'
variant='light'>
Github
</Button>
<Button
component='a'
href='mailto:m.refansa.am@gmail.com'
leftSection={<IconMail />}
size='lg'
variant='light'>
Email
</Button>
</Group>
</Box>
)
}
export default Introduction

View File

@ -0,0 +1,9 @@
.button {
transition: all linear 150ms;
}
.button {
&:active {
transform: scale(calc(0.95 * var(--mantine-scale)));
}
}

View File

@ -0,0 +1,8 @@
import { Button as Component } from '@mantine/core'
import classes from './Button.module.css'
export const Button = Component.extend({
defaultProps: {
className: classes.button,
},
})

View File

@ -0,0 +1,13 @@
.text {
@mixin dark {
&[data-variant='curvy'] {
text-decoration-style: wavy;
text-decoration-color: var(--mantine-color-coffee-4);
text-decoration-line: underline;
}
&[data-variant='shadow'] {
text-shadow: 0.25rem 0.25rem var(--mantine-color-dark-4);
}
}
}

View File

@ -0,0 +1,9 @@
import { Text as Component } from '@mantine/core'
import classes from './Text.module.css'
export const Text = Component.extend({
defaultProps: {
className: classes.text,
},
})

View File

@ -0,0 +1,5 @@
.input {
@mixin dark {
background-color: var(--mantine-color-dark-6);
}
}

View File

@ -0,0 +1,11 @@
import { TextInput as Component } from '@mantine/core'
import classes from './TextInput.module.css'
export const TextInput = Component.extend({
defaultProps: {
classNames: {
input: classes.input,
},
},
})

View File

@ -0,0 +1,5 @@
.input {
@mixin dark {
background-color: var(--mantine-color-dark-6);
}
}

View File

@ -0,0 +1,11 @@
import { Textarea as Component } from '@mantine/core'
import classes from './Textarea.module.css'
export const Textarea = Component.extend({
defaultProps: {
classNames: {
input: classes.input,
},
},
})

View File

@ -0,0 +1,13 @@
.title {
@mixin dark {
&[data-variant='curvy'] {
text-decoration-style: wavy;
text-decoration-color: var(--mantine-color-coffee-4);
text-decoration-line: underline;
}
&[data-variant='shadow'] {
text-shadow: 0.25rem 0.25rem var(--mantine-color-dark-4);
}
}
}

View File

@ -0,0 +1,9 @@
import { Title as Component } from '@mantine/core'
import classes from './Title.module.css'
export const Title = Component.extend({
defaultProps: {
className: classes.title,
},
})

View File

@ -0,0 +1,5 @@
export { Button } from './Button'
export { Text } from './Text'
export { Textarea } from './Textarea'
export { TextInput } from './TextInput'
export { Title } from './Title'

168
src/styles/theme.ts Normal file
View File

@ -0,0 +1,168 @@
'use client'
import {
CSSVariablesResolver,
MantineColorsTuple,
createTheme,
} from '@mantine/core'
import { fluidDisplay, fluidTypography } from '../utils/typography'
import { Button, Text, TextInput, Textarea, Title } from './extends'
const coffee: MantineColorsTuple = [
'#fff4e5',
'#f4e7d8',
'#e5cfb5',
'#d4b590',
'#c59e6f',
'#bd905a',
'#ba894f',
'#a3763e',
'#926835',
'#805928',
]
export const theme = createTheme({
// Controls focus ring styles. Supports the following options:
// - `auto` focus ring is displayed only when the user navigates with keyboard (default value)
// - `always` focus ring is displayed when the user navigates with keyboard and mouse
// - `never` focus ring is always hidden (not recommended)
//
focusRing: 'auto',
// rem units scale, change if you customize font-size of `<html />` element
// default value is `1` (for `100%`/`16px` font-size on `<html />`)
//
scale: 1,
// Determines whether `font-smoothing` property should be set on the body, `true` by default
fontSmoothing: true,
// White color
white: '#ffffff',
// Black color
black: '#000000',
// Object of colors, key is color name, value is an array of at least 10 strings (colors)
colors: {
coffee,
},
// Index of theme.colors[color].
// Primary shade is used in all components to determine which color from theme.colors[color] should be used.
// Can be either a number (09) or an object to specify different color shades for light and dark color schemes.
// Default value `{ light: 6, dark: 8 }`
//
// For example,
// { primaryShade: 6 } // shade 6 is used both for dark and light color schemes
// { primaryShade: { light: 6, dark: 7 } } // different shades for dark and light color schemes
//
primaryShade: { light: 4, dark: 8 },
// Key of `theme.colors`, hex/rgb/hsl values are not supported.
// Determines which color will be used in all components by default.
// Default value `blue`.
//
primaryColor: 'coffee',
// Function to resolve colors based on variant.
// Can be used to deeply customize how colors are applied to `Button`, `ActionIcon`, `ThemeIcon`
// and other components that use colors from theme.
//
// variantColorResolver: VariantColorsResolver;
// font-family used in all components, system fonts by default
fontFamily:
'Poppins, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji',
// Monospace font-family, used in code and other similar components, system fonts by default
fontFamilyMonospace:
'Fira Code, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace',
// Controls various styles of h1-h6 elements, used in TypographyStylesProvider and Title components
headings: {
fontFamily:
'Gabarito, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji',
},
// Object of values that are used to set `border-radius` in all components that support it
// radius: MantineRadiusValues;
// Key of `theme.radius` or any valid CSS value. Default `border-radius` used by most components
defaultRadius: 0,
// Object of values that are used to set various CSS properties that control spacing between elements
// spacing: MantineSpacingValues;
// Object of values that are used to control `font-size` property in all components
fontSizes: {
'fluid-xs': fluidTypography(8, 12, 480, 1440),
'fluid-sm': fluidTypography(10, 14, 480, 1440),
'fluid-md': fluidTypography(12, 16, 480, 1440),
'fluid-lg': fluidTypography(14, 18, 480, 1440),
'fluid-xl': fluidTypography(16, 20, 480, 1440),
'display-xl': fluidDisplay(80, 144, 768, 1920),
'display-lg': fluidDisplay(24, 36, 768, 1920),
'display-md': fluidDisplay(16, 24, 768, 1920),
},
// Object of values that are used to control `line-height` property in `Text` component
// lineHeights: MantineLineHeightValues;
// Object of values that are used to control breakpoints in all components,
// values are expected to be defined in em
//
// breakpoints: MantineBreakpointsValues;
// Object of values that are used to add `box-shadow` styles to components that support `shadow` prop
// shadows: MantineShadowsValues;
// Determines whether user OS settings to reduce motion should be respected, `false` by default
// respectReducedMotion: boolean;
// Determines which cursor type will be used for interactive elements
// - `default` cursor that is used by native HTML elements, for example, `input[type="checkbox"]` has `cursor: default` styles
// - `pointer` sets `cursor: pointer` on interactive elements that do not have these styles by default
//
cursorType: 'pointer',
// Default gradient configuration for components that support `variant="gradient"`
// defaultGradient: MantineGradient;
// Class added to the elements that have active styles, for example, `Button` and `ActionIcon`
// activeClassName: string;
// Class added to the elements that have focus styles, for example, `Button` or `ActionIcon`.
// Overrides `theme.focusRing` property.
//
// focusClassName: string;
// Allows adding `classNames`, `styles` and `defaultProps` to any component
components: {
Button,
Text,
Textarea,
TextInput,
Title,
},
// Any other properties that you want to access with the theme objects
other: {
bodyLight: '#ffffff',
textLight: '#000000',
bodyDark: '#000000',
textDark: '#ffffff',
},
})
export const resolver: CSSVariablesResolver = (t) => ({
variables: {},
light: {
'--mantine-color-body': t.other.bodyLight,
'--mantine-color-text': t.other.textLight,
},
dark: {
'--mantine-color-body': t.other.bodyDark,
'--mantine-color-text': t.other.textDark,
},
})

58
src/utils/typography.ts Normal file
View File

@ -0,0 +1,58 @@
export const PIXEL_PER_REM = 16
/**
* Returns a number from pixel to rem.
*
* @param value The value in pixel.
*/
export const rem = (value: number): number => {
return value / PIXEL_PER_REM
}
/**
* Returns a string of calculated value used for font size in display typography.
*
* Formula based from https://github.com/abdulrcs/abdulrahman.id/blob/main/styles/theme.js
*
* @param minFont Minimal font size in pixel.
* @param maxFont Maximum font size in pixel.
* @param minVW Minimum font size in pixel.
* @param maxVW Maximum font size in pixel.
*/
export const fluidDisplay = (
minFont: number,
maxFont: number,
minVW: number,
maxVW: number,
) => {
let XX = minVW / 100
let YY = (100 * (maxFont - minFont)) / (maxVW - minVW)
let ZZ = rem(minFont)
return `calc(${ZZ}rem + ((1vw - ${XX}px) * ${YY}))`
}
/**
* Returns a string of clamp value used for font size in normal typography.
*
* Formula based from https://www.aleksandrhovhannisyan.com/blog/fluid-type-scale-with-css-clamp/
*
* @param minFont Minimal font size in pixel.
* @param maxFont Maximum font size in pixel.
* @param minVW Minimum font size in pixel.
* @param maxVW Maximum font size in pixel.
*/
export const fluidTypography = (
minFont: number,
maxFont: number,
minVW: number,
maxVW: number,
): string => {
const slope = (maxFont - minFont) / (maxVW - minVW)
const intercept = rem(minFont - slope * minVW)
const slopeVW = slope * 100
const minSize = rem(minFont)
const maxSize = rem(maxFont)
return `clamp(${minSize}rem, ${slopeVW}vw + ${intercept}rem, ${maxSize}rem)`
}

View File

@ -1,7 +0,0 @@
"use client";
import { createTheme } from "@mantine/core";
export const theme = createTheme({
/* Put your mantine theme override here */
});