Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions rspack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ module.exports = {
entry: {
main: "./src/main.bs.js",
},
devServer: {
historyApiFallback: true,
},
devtool: "source-map",
module: {
rules: [
Expand Down
266 changes: 15 additions & 251 deletions src/App.res
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
// Import design system and components
open Emotion.Css

// Import component library
module Components = {
module Button = Button.Button
module Input = Input.Input
module Card = Card.Card
module Typography = Typography.Typography
}

// Initialize global styles
let _ = GlobalStyles.initialize()

Expand All @@ -24,7 +15,7 @@ Emotion.Css.injectGlobal(`
will-change: filter;
}
.logo:hover {
filter: drop-shadow(0 0 2em #c8a45caa);
filter: drop-shadow(0 0 2em ${Theme.Theme.Colors.brand["primary"]}aa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
Expand All @@ -47,7 +38,7 @@ Emotion.Css.injectGlobal(`

/* Hero section gradients */
.hero-gradient {
background: linear-gradient(135deg, #c8a45c 0%, #c47d4a 100%);
background: linear-gradient(135deg, ${Theme.Theme.Colors.brand["primary"]} 0%, ${Theme.Theme.Colors.brand["secondary"]} 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
Expand All @@ -66,251 +57,24 @@ Emotion.Css.injectGlobal(`
}
`)

module Logo = {
@module("./assets/react.svg") @react.component
external make: (~role: string=?, ~className: string=?, ~alt: string=?, unit) => React.element =
"default"
}

// Homepage sections
module HeroSection = {
@react.component
let make = () => {
let (count, setCount) = React.useState(() => 0)

let heroStyles = css({
"minHeight": "80vh",
"display": "flex",
"flexDirection": "column",
"alignItems": "center",
"justifyContent": "center",
"textAlign": "center",
"padding": "2rem 1rem",
"background": "linear-gradient(180deg, rgba(200, 164, 92, 0.04) 0%, transparent 100%)"
})

let logoContainerStyles = css({
"display": "flex",
"gap": "2rem",
"marginBottom": "2rem",
"alignItems": "center"
})

<section className={heroStyles}>
<div className={logoContainerStyles}>
<a href="https://reactjs.org" target="_blank">
<Logo className="logo react" alt="React logo" />
</a>
</div>

<Components.Typography.Display size=#"4xl" className="hero-gradient">
{React.string("Modern React Development")}
</Components.Typography.Display>

<Components.Typography.Heading level=#h2 size=#xl className={css({"color": Theme.Theme.Colors.text["secondary"], "marginTop": "1rem", "maxWidth": "600px"})}>
{React.string("Built with Rspack, ReScript, React, and Bun for lightning-fast development")}
</Components.Typography.Heading>

<div className={css({"display": "flex", "gap": "1rem", "marginTop": "2rem", "flexWrap": "wrap", "justifyContent": "center"})}>
<Components.Button
variant=#primary
size=#lg
onClick={_evt => setCount(prev => prev + 1)}>
{React.string("Interactive Demo (" ++ Int.toString(count) ++ ")")}
</Components.Button>
<Components.Button variant=#outline size=#lg>
{React.string("View Components")}
</Components.Button>
</div>
</section>
}
}

module FeaturesSection = {
@react.component
let make = () => {
let sectionStyles = css({
"padding": "4rem 1rem",
"maxWidth": "1200px",
"margin": "0 auto"
})

let gridStyles = css({
"display": "grid",
"gridTemplateColumns": "repeat(auto-fit, minmax(300px, 1fr))",
"gap": "2rem",
"marginTop": "3rem"
})

let features = [
("⚡ Lightning Fast", "Built with Rspack for ultra-fast bundling and hot reload"),
("🔧 Type Safety", "ReScript provides bulletproof type safety and excellent ergonomics"),
("🎨 Design System", "Comprehensive component library with consistent styling"),
("🚀 Modern Stack", "Latest React, Emotion CSS-in-JS, and Bun package manager")
]

<section className={sectionStyles}>
<Components.Typography.Display size=#"3xl" align=#center>
{React.string("Why This Template?")}
</Components.Typography.Display>

<div className={gridStyles}>
{features
->Belt.Array.map(((title, description)) => {
<Components.Card key={title} variant=#elevated>
<Components.Typography.Heading level=#h3 size=#lg>
{React.string(title)}
</Components.Typography.Heading>
<Components.Typography.Body>
{React.string(description)}
</Components.Typography.Body>
</Components.Card>
})
->React.array}
</div>
</section>
}
}

module ComponentsSection = {
@react.component
let make = () => {
let (inputValue, setInputValue) = React.useState(() => "")
let (_selectedCard, _setSelectedCard) = React.useState(() => None)

let sectionStyles = css({
"padding": "4rem 1rem",
"backgroundColor": `${Theme.Theme.Colors.background["elevated"]}80`,
"borderTop": `1px solid ${Theme.Theme.Colors.border["default"]}`
})

let containerStyles = css({
"maxWidth": "1200px",
"margin": "0 auto"
})

let demoGridStyles = css({
"display": "grid",
"gridTemplateColumns": "repeat(auto-fit, minmax(350px, 1fr))",
"gap": "2rem",
"marginTop": "3rem"
})

<section className={sectionStyles}>
<div className={containerStyles}>
<Components.Typography.Display size=#"3xl" align=#center>
{React.string("Component Library")}
</Components.Typography.Display>

<Components.Typography.Body size=#lg align=#center className={css({"marginTop": "1rem", "color": Theme.Theme.Colors.text["secondary"]})}>
{React.string("Explore our comprehensive component library with variants, sizes, and states")}
</Components.Typography.Body>

<div className={demoGridStyles}>
// Button Variants Demo
<Components.Card variant=#outlined>
<Components.Card.Header>
<Components.Typography.Heading level=#h3>
{React.string("Button Variants")}
</Components.Typography.Heading>
</Components.Card.Header>
<Components.Card.Body>
<div className={css({"display": "flex", "flexDirection": "column", "gap": "1rem"})}>
<Components.Button variant=#primary>
{React.string("Primary")}
</Components.Button>
<Components.Button variant=#secondary>
{React.string("Secondary")}
</Components.Button>
<Components.Button variant=#outline>
{React.string("Outline")}
</Components.Button>
<Components.Button variant=#ghost>
{React.string("Ghost")}
</Components.Button>
<Components.Button variant=#danger>
{React.string("Danger")}
</Components.Button>
</div>
</Components.Card.Body>
</Components.Card>

// Input Variants Demo
<Components.Card variant=#outlined>
<Components.Card.Header>
<Components.Typography.Heading level=#h3>
{React.string("Input Variants")}
</Components.Typography.Heading>
</Components.Card.Header>
<Components.Card.Body>
<div className={css({"display": "flex", "flexDirection": "column", "gap": "1rem"})}>
<Components.Input
variant=#default
placeholder="Default input"
value={inputValue}
onChange={evt => {
let value = ReactEvent.Form.target(evt)["value"]
setInputValue(_ => value)
}}
/>
<Components.Input
variant=#filled
placeholder="Filled input"
/>
<Components.Input
variant=#outlined
placeholder="Outlined input"
/>
<Components.Input
variant=#flushed
placeholder="Flushed input"
/>
</div>
</Components.Card.Body>
</Components.Card>

// Typography Demo
<Components.Card variant=#outlined>
<Components.Card.Header>
<Components.Typography.Heading level=#h3>
{React.string("Typography System")}
</Components.Typography.Heading>
</Components.Card.Header>
<Components.Card.Body>
<div className={css({"display": "flex", "flexDirection": "column", "gap": "1rem", "textAlign": "left"})}>
<Components.Typography.Display size=#lg>
{React.string("Display Text")}
</Components.Typography.Display>
<Components.Typography.Heading level=#h2>
{React.string("Heading Text")}
</Components.Typography.Heading>
<Components.Typography.Body>
{React.string("Body text with proper line height and spacing for optimal readability.")}
</Components.Typography.Body>
<Components.Typography.Caption>
{React.string("Caption text for additional information")}
</Components.Typography.Caption>
</div>
</Components.Card.Body>
</Components.Card>
</div>
</div>
</section>
}
}

module App = {
@react.component
let make = () => {
<div>
<HeroSection />
<FeaturesSection />
<ComponentsSection />

<footer className={css({"padding": "2rem", "textAlign": "center", "borderTop": `1px solid ${Theme.Theme.Colors.border["default"]}`, "marginTop": "2rem"})}>
<Components.Typography.Caption>
<NavBar.NavBar />
<main>
<Router />
</main>
<footer
className={css({
"padding": "2rem",
"textAlign": "center",
"borderTop": `1px solid ${Theme.Theme.Colors.border["default"]}`,
"marginTop": "2rem",
})}>
<Typography.Typography.Caption>
{React.string("Built with Rspack + ReScript + React + Bun")}
</Components.Typography.Caption>
</Typography.Typography.Caption>
</footer>
</div>
}
Expand Down
43 changes: 43 additions & 0 deletions src/Router.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
open Emotion.Css

module NotFound = {
@react.component
let make = () => {
let containerStyles = css({
"padding": "4rem 1rem",
"maxWidth": "600px",
"margin": "0 auto",
"textAlign": "center",
})

<div className={containerStyles}>
<Typography.Typography.Display size=#"4xl">
{React.string("404")}
</Typography.Typography.Display>
<Typography.Typography.Body
className={css({"marginTop": "1rem", "color": Theme.Theme.Colors.text["secondary"]})}>
{React.string("Page not found.")}
</Typography.Typography.Body>
<div className={css({"marginTop": "2rem"})}>
<Button.Button.make
variant=#primary
onClick={_evt => RescriptReactRouter.push("/")}>
{React.string("Go Home")}
</Button.Button.make>
</div>
</div>
}
}

@react.component
let make = () => {
let url = RescriptReactRouter.useUrl()

switch url.path {
| list{} => <HomePage />
| list{"components"} => <ComponentsPage />
| list{"posts"} => <PostsPage />
| list{"login"} => <LoginPage />
| _ => <NotFound />
}
}
Loading