shortcut-next 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -2
- package/bin/{quickstart-next.mjs → shortcut-next.mjs} +1 -1
- package/package.json +17 -7
- package/templates/base/@core/theme/ThemeComponent.tsx +42 -0
- package/templates/base/@core/theme/ThemeOptions.ts +21 -0
- package/templates/base/@core/theme/breakpoints/index.ts +7 -0
- package/templates/base/@core/theme/globalStyles.tsx +13 -0
- package/templates/base/@core/theme/overrides/index.ts +58 -0
- package/templates/base/@core/theme/palette/index.ts +37 -0
- package/templates/base/@core/theme/shadows/index.ts +13 -0
- package/templates/base/@core/theme/spacing/index.ts +5 -0
- package/templates/base/@core/theme/types.ts +10 -0
- package/templates/base/@core/theme/typography/index.ts +15 -0
- package/templates/base/app/layout.tsx +2 -2
- package/templates/base/app/page.tsx +214 -200
- package/templates/base/providers/BaseProvider.tsx +3 -11
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Hadi87s
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Shortcut Next ⚡
|
|
2
2
|
|
|
3
3
|
> Scaffold modern **Next.js 15+ projects** in seconds — with **MUI**, **React Hook Form**, and **TanStack Query** built-in.
|
|
4
4
|
> Optionally add **Tailwind CSS v4** with a single command.
|
|
@@ -22,4 +22,5 @@
|
|
|
22
22
|
You don’t need to install globally. Use `npx`:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
npx
|
|
25
|
+
npx shortcut-next@latest
|
|
26
|
+
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shortcut-next",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Scaffold Next.js apps with MUI base or Tailwind v4 preset.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"shortcut-next": "bin/
|
|
7
|
+
"shortcut-next": "bin/shortcut-next.mjs"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/hadi87s/
|
|
11
|
+
"url": "git+https://github.com/hadi87s/shortcut-next.git"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/hadi87s/
|
|
14
|
+
"url": "https://github.com/hadi87s/shortcut-next/issues"
|
|
15
15
|
},
|
|
16
|
-
"homepage": "https://github.com/hadi87s/
|
|
16
|
+
"homepage": "https://github.com/hadi87s/shortcut-next#readme",
|
|
17
17
|
"publishConfig": {
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
@@ -26,9 +26,19 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
28
28
|
},
|
|
29
|
-
"
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"nextjs",
|
|
34
|
+
"cli",
|
|
35
|
+
"scaffold",
|
|
36
|
+
"starter",
|
|
37
|
+
"mui",
|
|
38
|
+
"tailwind"
|
|
39
|
+
],
|
|
30
40
|
"author": "",
|
|
31
|
-
"license": "
|
|
41
|
+
"license": "MIT",
|
|
32
42
|
"type": "module",
|
|
33
43
|
"dependencies": {
|
|
34
44
|
"@clack/prompts": "^0.11.0",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { CssBaseline, PaletteMode, ThemeProvider, createTheme } from '@mui/material'
|
|
5
|
+
import GlobalStyles from './globalStyles'
|
|
6
|
+
import { buildThemeOptions } from './ThemeOptions'
|
|
7
|
+
|
|
8
|
+
type Ctx = { mode: PaletteMode; toggle: () => void; set: (m: PaletteMode) => void }
|
|
9
|
+
export const ColorModeContext = React.createContext<Ctx>({ mode: 'dark', toggle: () => {}, set: () => {} })
|
|
10
|
+
|
|
11
|
+
function getInitialMode(): PaletteMode {
|
|
12
|
+
if (typeof window === 'undefined') return 'dark'
|
|
13
|
+
const stored = window.localStorage.getItem('color-scheme') as PaletteMode | null
|
|
14
|
+
if (stored === 'light' || stored === 'dark') return stored
|
|
15
|
+
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function ThemeComponent({ children }: { children: React.ReactNode }) {
|
|
19
|
+
const [mode, setMode] = React.useState<PaletteMode>(getInitialMode)
|
|
20
|
+
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
document.documentElement.setAttribute('data-color-scheme', mode)
|
|
23
|
+
window.localStorage.setItem('color-scheme', mode)
|
|
24
|
+
}, [mode])
|
|
25
|
+
|
|
26
|
+
const ctx = React.useMemo<Ctx>(
|
|
27
|
+
() => ({ mode, toggle: () => setMode(m => (m === 'light' ? 'dark' : 'light')), set: setMode }),
|
|
28
|
+
[mode]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const theme = React.useMemo(() => createTheme(buildThemeOptions(mode)), [mode])
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<ColorModeContext.Provider value={ctx}>
|
|
35
|
+
<ThemeProvider theme={theme}>
|
|
36
|
+
<CssBaseline />
|
|
37
|
+
<GlobalStyles />
|
|
38
|
+
{children}
|
|
39
|
+
</ThemeProvider>
|
|
40
|
+
</ColorModeContext.Provider>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createTheme, PaletteMode, ThemeOptions } from '@mui/material'
|
|
2
|
+
import { makePalette } from './palette'
|
|
3
|
+
import { makeTypography } from './typography'
|
|
4
|
+
import { makeShadows } from './shadows'
|
|
5
|
+
import { makeSpacing } from './spacing'
|
|
6
|
+
import { makeBreakpoints } from './breakpoints'
|
|
7
|
+
import { makeOverrides } from './overrides'
|
|
8
|
+
|
|
9
|
+
export function buildThemeOptions(mode: PaletteMode): ThemeOptions {
|
|
10
|
+
const options: ThemeOptions = {
|
|
11
|
+
palette: makePalette(mode),
|
|
12
|
+
typography: makeTypography(),
|
|
13
|
+
spacing: makeSpacing(),
|
|
14
|
+
breakpoints: makeBreakpoints(),
|
|
15
|
+
shape: { borderRadius: 12 },
|
|
16
|
+
shadows: makeShadows(),
|
|
17
|
+
components: makeOverrides(createTheme({ palette: makePalette(mode) }))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return options
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { GlobalStyles as MUIGlobalStyles } from '@mui/material'
|
|
3
|
+
|
|
4
|
+
export default function GlobalStyles() {
|
|
5
|
+
return (
|
|
6
|
+
<MUIGlobalStyles
|
|
7
|
+
styles={{
|
|
8
|
+
'*, *::before, *::after': { boxSizing: 'border-box' },
|
|
9
|
+
body: { margin: 0 }
|
|
10
|
+
}}
|
|
11
|
+
/>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Components, Theme } from '@mui/material/styles'
|
|
2
|
+
|
|
3
|
+
export function makeOverrides(theme: Theme): Components<Theme> {
|
|
4
|
+
const isDark = theme.palette.mode === 'dark'
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
MuiCssBaseline: {
|
|
8
|
+
styleOverrides: {
|
|
9
|
+
':root': {
|
|
10
|
+
'--blob-1': 'radial-gradient(closest-side, #7C4DFF 0%, rgba(124,77,255,0) 70%)',
|
|
11
|
+
'--blob-2': 'radial-gradient(closest-side, #00E5FF 0%, rgba(0,229,255,0) 70%)'
|
|
12
|
+
},
|
|
13
|
+
body: {
|
|
14
|
+
backgroundImage: isDark
|
|
15
|
+
? `radial-gradient(1200px 600px at 10% -10%, rgba(124,77,255,0.07), transparent),
|
|
16
|
+
radial-gradient(900px 500px at 110% 110%, rgba(0,229,255,0.06), transparent)`
|
|
17
|
+
: undefined
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
MuiContainer: { defaultProps: { maxWidth: 'lg' } },
|
|
23
|
+
|
|
24
|
+
MuiCard: {
|
|
25
|
+
styleOverrides: {
|
|
26
|
+
root: {
|
|
27
|
+
borderRadius: 12,
|
|
28
|
+
border: `1px solid ${isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)'}`,
|
|
29
|
+
background: isDark
|
|
30
|
+
? 'linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02))'
|
|
31
|
+
: 'linear-gradient(180deg, #FFFFFF, #FAFBFF)'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
MuiButton: {
|
|
37
|
+
defaultProps: { disableElevation: true },
|
|
38
|
+
styleOverrides: {
|
|
39
|
+
root: { borderRadius: 12, paddingInline: 16 },
|
|
40
|
+
containedPrimary: {
|
|
41
|
+
background: `linear-gradient(180deg, ${theme.palette.primary.main}, ${theme.palette.primary.dark})`
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
MuiTextField: { defaultProps: { size: 'small', fullWidth: true } },
|
|
47
|
+
MuiInputLabel: { styleOverrides: { root: { fontWeight: 600 } } },
|
|
48
|
+
MuiChip: { styleOverrides: { root: { borderRadius: 10 } } },
|
|
49
|
+
MuiAppBar: {
|
|
50
|
+
styleOverrides: {
|
|
51
|
+
root: {
|
|
52
|
+
backgroundColor: isDark ? 'rgba(16,19,26,0.72)' : 'rgba(255,255,255,0.72)',
|
|
53
|
+
backdropFilter: 'saturate(120%) blur(8px)'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PaletteMode, ThemeOptions } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
const brand = {
|
|
4
|
+
50: '#EEF0FF',
|
|
5
|
+
100: '#DDE2FF',
|
|
6
|
+
200: '#BEC7FF',
|
|
7
|
+
300: '#9DABFF',
|
|
8
|
+
400: '#7C8FFF',
|
|
9
|
+
500: '#5B74FF', // main
|
|
10
|
+
600: '#415BEE',
|
|
11
|
+
700: '#2F46CF',
|
|
12
|
+
800: '#2132A6',
|
|
13
|
+
900: '#1A2880'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function makePalette(mode: PaletteMode): ThemeOptions['palette'] {
|
|
17
|
+
const isDark = mode === 'dark'
|
|
18
|
+
return {
|
|
19
|
+
mode,
|
|
20
|
+
primary: { light: brand[400], main: brand[500], dark: brand[700], contrastText: '#fff' },
|
|
21
|
+
brand: { light: brand[400], main: brand[500], dark: brand[700], contrastText: '#fff' },
|
|
22
|
+
secondary: { light: '#64E1FF', main: '#00D0FF', dark: '#00A3CC', contrastText: '#001219' },
|
|
23
|
+
error: { light: '#FF7A7A', main: '#FF4D4F', dark: '#C62828' },
|
|
24
|
+
warning: { light: '#FFD166', main: '#FFB703', dark: '#C98A00' },
|
|
25
|
+
info: { light: '#9AD0FF', main: '#55ADFF', dark: '#1E7ED6' },
|
|
26
|
+
success: { light: '#33D69F', main: '#11C28B', dark: '#0E9B6F' },
|
|
27
|
+
divider: isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.12)',
|
|
28
|
+
background: {
|
|
29
|
+
default: isDark ? '#0B0D12' : '#F7F8FB',
|
|
30
|
+
paper: isDark ? '#10131A' : '#FFFFFF'
|
|
31
|
+
},
|
|
32
|
+
text: {
|
|
33
|
+
primary: isDark ? '#E6E8EF' : '#10141D',
|
|
34
|
+
secondary: isDark ? 'rgba(230,232,239,0.7)' : '#4A5568'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ThemeOptions } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
export function makeShadows(): ThemeOptions['shadows'] {
|
|
4
|
+
// start from MUI defaults and tweak a few
|
|
5
|
+
const base = [
|
|
6
|
+
'none',
|
|
7
|
+
'0 1px 3px rgba(0,0,0,0.12)',
|
|
8
|
+
'0 2px 6px rgba(0,0,0,0.12)',
|
|
9
|
+
'0 4px 12px rgba(0,0,0,0.12)',
|
|
10
|
+
...Array(21).fill('0 10px 30px rgba(0,0,0,0.12)')
|
|
11
|
+
] as unknown as ThemeOptions['shadows']
|
|
12
|
+
return base
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ThemeOptions } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
export function makeTypography(): ThemeOptions['typography'] {
|
|
4
|
+
return {
|
|
5
|
+
fontFamily:
|
|
6
|
+
'Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, "Apple Color Emoji","Segoe UI Emoji"',
|
|
7
|
+
h1: { fontWeight: 800, letterSpacing: 0.2 },
|
|
8
|
+
h2: { fontWeight: 800, letterSpacing: 0.2 },
|
|
9
|
+
h3: { fontWeight: 800 },
|
|
10
|
+
h4: { fontWeight: 700 },
|
|
11
|
+
h5: { fontWeight: 700 },
|
|
12
|
+
h6: { fontWeight: 700 },
|
|
13
|
+
button: { textTransform: 'none', fontWeight: 600, letterSpacing: 0.2 }
|
|
14
|
+
} as const
|
|
15
|
+
}
|
|
@@ -14,8 +14,8 @@ const geistMono = Geist_Mono({
|
|
|
14
14
|
})
|
|
15
15
|
|
|
16
16
|
export const metadata: Metadata = {
|
|
17
|
-
title: '
|
|
18
|
-
description: '
|
|
17
|
+
title: 'Shortcut Nextjs Template',
|
|
18
|
+
description: 'Stop starting projects from scratch, start in the middle and save time!'
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export default function RootLayout({
|
|
@@ -18,8 +18,9 @@ import {
|
|
|
18
18
|
} from '@mui/material'
|
|
19
19
|
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
|
|
20
20
|
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
|
|
21
|
-
import { Github, Package, LayoutDashboard, FormInput } from 'lucide-react'
|
|
21
|
+
import { Github, Package, LayoutDashboard, FormInput, Sun, Moon } from 'lucide-react'
|
|
22
22
|
import { Icon } from '@iconify/react'
|
|
23
|
+
import { ColorModeContext } from '@/@core/theme/ThemeComponent'
|
|
23
24
|
|
|
24
25
|
const Code = ({ children }: { children: React.ReactNode }) => (
|
|
25
26
|
<Box
|
|
@@ -41,6 +42,213 @@ const Code = ({ children }: { children: React.ReactNode }) => (
|
|
|
41
42
|
</Box>
|
|
42
43
|
)
|
|
43
44
|
|
|
45
|
+
function HeroSection({}: { copied: boolean; handleCopy: (text: string) => void }) {
|
|
46
|
+
const colorMode = React.useContext(ColorModeContext)
|
|
47
|
+
const { mode, toggle } = colorMode || { mode: 'light', toggle: () => {} }
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Stack spacing={3} alignItems='center' textAlign='center' sx={{ mb: { xs: 6, md: 10 } }}>
|
|
51
|
+
<Stack direction='row' spacing={1} alignItems='center'>
|
|
52
|
+
<LayoutDashboard size={28} style={{ verticalAlign: 'middle' }} />
|
|
53
|
+
<Typography variant='h4' fontWeight={800} letterSpacing={0.2} sx={{ ml: 1 }}>
|
|
54
|
+
Shortcut Next
|
|
55
|
+
</Typography>
|
|
56
|
+
</Stack>
|
|
57
|
+
<Typography variant='h6' sx={{ maxWidth: 860, opacity: 0.9 }}>
|
|
58
|
+
A modern Next.js boilerplate powered by <b>MUI</b> with room for <b>React Query</b>, <b>React Hook Form</b>, and
|
|
59
|
+
optional <b>Tailwind v4</b>.
|
|
60
|
+
</Typography>
|
|
61
|
+
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} useFlexGap flexWrap='wrap'>
|
|
62
|
+
<Chip
|
|
63
|
+
icon={<Icon icon='simple-icons:mui' width={18} height={18} style={{ borderRadius: 4 }} />}
|
|
64
|
+
label='MUI'
|
|
65
|
+
color='primary'
|
|
66
|
+
variant='filled'
|
|
67
|
+
/>
|
|
68
|
+
<Chip icon={<FormInput size={18} />} label='React Hook Form' variant='outlined' />
|
|
69
|
+
<Chip
|
|
70
|
+
icon={<Icon icon='devicon:tailwindcss' width={18} height={18} />}
|
|
71
|
+
label='Tailwind v4 (optional)'
|
|
72
|
+
variant='outlined'
|
|
73
|
+
/>
|
|
74
|
+
<Chip
|
|
75
|
+
icon={<Icon icon='devicon:typescript' width={18} height={18} style={{ borderRadius: 4 }} />}
|
|
76
|
+
label='TypeScript'
|
|
77
|
+
variant='outlined'
|
|
78
|
+
/>
|
|
79
|
+
<Chip icon={<Icon icon='devicon:nextjs' width={18} height={18} />} label='App Router' variant='outlined' />
|
|
80
|
+
</Stack>
|
|
81
|
+
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ pt: 1 }}>
|
|
82
|
+
<Button
|
|
83
|
+
size='large'
|
|
84
|
+
variant='contained'
|
|
85
|
+
onClick={toggle}
|
|
86
|
+
startIcon={mode === 'dark' ? <Sun size={20} /> : <Moon size={20} />}
|
|
87
|
+
>
|
|
88
|
+
Toggle Theme ({mode === 'dark' ? 'Dark' : 'Light'})
|
|
89
|
+
</Button>
|
|
90
|
+
<Button
|
|
91
|
+
size='large'
|
|
92
|
+
variant='outlined'
|
|
93
|
+
endIcon={<OpenInNewIcon />}
|
|
94
|
+
component={Link}
|
|
95
|
+
href='#'
|
|
96
|
+
target='_blank'
|
|
97
|
+
rel='noopener'
|
|
98
|
+
>
|
|
99
|
+
View Docs
|
|
100
|
+
</Button>
|
|
101
|
+
</Stack>
|
|
102
|
+
</Stack>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function WhatsIncludedCard({ copied, handleCopy }: { copied: boolean; handleCopy: (text: string) => void }) {
|
|
107
|
+
return (
|
|
108
|
+
<Card
|
|
109
|
+
sx={{
|
|
110
|
+
backdropFilter: 'saturate(120%) blur(6px)',
|
|
111
|
+
background: 'rgba(255,255,255,0.04)',
|
|
112
|
+
border: '1px solid',
|
|
113
|
+
borderColor: 'rgba(255,255,255,0.08)'
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
<CardContent>
|
|
117
|
+
<Typography variant='h6' fontWeight={700} gutterBottom>
|
|
118
|
+
What’s included
|
|
119
|
+
</Typography>
|
|
120
|
+
<Stack spacing={1.25} sx={{ opacity: 0.9 }}>
|
|
121
|
+
<Typography>• Next.js 15 (App Router) + TypeScript</Typography>
|
|
122
|
+
<Typography>• MUI ThemeProvider + dark-ready UI</Typography>
|
|
123
|
+
<Typography>
|
|
124
|
+
• RHF starter form at <code>/sample-form</code>
|
|
125
|
+
</Typography>
|
|
126
|
+
<Typography>• Easy opt-in Tailwind v4 (via CLI preset)</Typography>
|
|
127
|
+
</Stack>
|
|
128
|
+
<Divider sx={{ my: 3 }} />
|
|
129
|
+
<Typography variant='subtitle2' gutterBottom>
|
|
130
|
+
Scaffold via npx
|
|
131
|
+
</Typography>
|
|
132
|
+
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} alignItems='center'>
|
|
133
|
+
<Code>npx shortcut-next@latest</Code>
|
|
134
|
+
<Tooltip title={copied ? 'Copied!' : 'Copy'}>
|
|
135
|
+
<Button
|
|
136
|
+
variant='outlined'
|
|
137
|
+
size='small'
|
|
138
|
+
startIcon={<ContentCopyIcon fontSize='small' />}
|
|
139
|
+
onClick={() => handleCopy('npx shortcut-next@latest')}
|
|
140
|
+
>
|
|
141
|
+
{copied ? 'Copied' : 'Copy'}
|
|
142
|
+
</Button>
|
|
143
|
+
</Tooltip>
|
|
144
|
+
</Stack>
|
|
145
|
+
</CardContent>
|
|
146
|
+
</Card>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function TechLogosCard() {
|
|
151
|
+
return (
|
|
152
|
+
<Card
|
|
153
|
+
sx={{
|
|
154
|
+
height: '100%',
|
|
155
|
+
backdropFilter: 'saturate(120%) blur(6px)',
|
|
156
|
+
background: 'rgba(255,255,255,0.04)',
|
|
157
|
+
border: '1px solid',
|
|
158
|
+
borderColor: 'rgba(255,255,255,0.08)'
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
<CardContent>
|
|
162
|
+
<Typography variant='h6' fontWeight={700} gutterBottom>
|
|
163
|
+
Current Tech
|
|
164
|
+
</Typography>
|
|
165
|
+
<Stack direction='row' spacing={2} alignItems='center' sx={{ pb: 1.5 }}>
|
|
166
|
+
<Tooltip title='MUI' arrow placement='top'>
|
|
167
|
+
<span>
|
|
168
|
+
<Icon icon='simple-icons:mui' width={28} height={28} style={{ borderRadius: 4 }} />
|
|
169
|
+
</span>
|
|
170
|
+
</Tooltip>
|
|
171
|
+
<Tooltip title='Tailwind CSS' arrow placement='top'>
|
|
172
|
+
<span>
|
|
173
|
+
<Icon icon='devicon:tailwindcss' width={28} height={28} />
|
|
174
|
+
</span>
|
|
175
|
+
</Tooltip>
|
|
176
|
+
<Tooltip title='React' arrow placement='top'>
|
|
177
|
+
<span>
|
|
178
|
+
<Icon icon='devicon:react' width={28} height={28} />
|
|
179
|
+
</span>
|
|
180
|
+
</Tooltip>
|
|
181
|
+
<Tooltip title='Next.js' arrow placement='top'>
|
|
182
|
+
<span>
|
|
183
|
+
<Icon icon='devicon:nextjs' width={28} height={28} />
|
|
184
|
+
</span>
|
|
185
|
+
</Tooltip>
|
|
186
|
+
<Tooltip title='React Hook Form' arrow placement='top'>
|
|
187
|
+
<span>
|
|
188
|
+
<Icon icon='simple-icons:reacthookform' width={28} height={28} />
|
|
189
|
+
</span>
|
|
190
|
+
</Tooltip>
|
|
191
|
+
<Tooltip title='TypeScript' arrow placement='top'>
|
|
192
|
+
<span>
|
|
193
|
+
<Icon icon='devicon:typescript' width={28} height={28} style={{ borderRadius: 4 }} />
|
|
194
|
+
</span>
|
|
195
|
+
</Tooltip>
|
|
196
|
+
</Stack>
|
|
197
|
+
<Typography variant='body2' sx={{ opacity: 0.85 }}>
|
|
198
|
+
These are the core technologies powering this template. Hover over each logo to see its name.
|
|
199
|
+
</Typography>
|
|
200
|
+
<Divider sx={{ my: 2 }} />
|
|
201
|
+
<Stack direction='row' spacing={1.5}>
|
|
202
|
+
<Button
|
|
203
|
+
variant='outlined'
|
|
204
|
+
size='small'
|
|
205
|
+
startIcon={<Github size={18} />}
|
|
206
|
+
endIcon={<OpenInNewIcon />}
|
|
207
|
+
component={Link}
|
|
208
|
+
href='https://github.com/Hadi87s/shortcut-next'
|
|
209
|
+
target='_blank'
|
|
210
|
+
rel='noopener'
|
|
211
|
+
>
|
|
212
|
+
GitHub
|
|
213
|
+
</Button>
|
|
214
|
+
<Button
|
|
215
|
+
variant='outlined'
|
|
216
|
+
size='small'
|
|
217
|
+
startIcon={<Package size={18} />}
|
|
218
|
+
endIcon={<OpenInNewIcon />}
|
|
219
|
+
component={Link}
|
|
220
|
+
href='https://www.npmjs.com/package/shortcut-next'
|
|
221
|
+
target='_blank'
|
|
222
|
+
rel='noopener'
|
|
223
|
+
>
|
|
224
|
+
npm
|
|
225
|
+
</Button>
|
|
226
|
+
</Stack>
|
|
227
|
+
</CardContent>
|
|
228
|
+
</Card>
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function Footer() {
|
|
233
|
+
return (
|
|
234
|
+
<Stack alignItems='center' sx={{ mt: 8, opacity: 0.65 }}>
|
|
235
|
+
<Typography variant='body2'>
|
|
236
|
+
Built with ❤️ by{' '}
|
|
237
|
+
<MuiLink
|
|
238
|
+
href='https://github.com/hadi87s/quickstart-next'
|
|
239
|
+
underline='none'
|
|
240
|
+
color='primary'
|
|
241
|
+
sx={{ fontWeight: 600 }}
|
|
242
|
+
target='_blank'
|
|
243
|
+
rel='noopener'
|
|
244
|
+
>
|
|
245
|
+
Hadi
|
|
246
|
+
</MuiLink>{' '}
|
|
247
|
+
using MUI. Ready for Tailwind v4, React Query, and more.
|
|
248
|
+
</Typography>
|
|
249
|
+
</Stack>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
44
252
|
export default function Page() {
|
|
45
253
|
const [copied, setCopied] = React.useState(false)
|
|
46
254
|
|
|
@@ -64,7 +272,6 @@ export default function Page() {
|
|
|
64
272
|
color: 'text.primary'
|
|
65
273
|
}}
|
|
66
274
|
>
|
|
67
|
-
{/* --- Glowing blobs --- */}
|
|
68
275
|
<Box
|
|
69
276
|
aria-hidden
|
|
70
277
|
sx={{
|
|
@@ -72,7 +279,7 @@ export default function Page() {
|
|
|
72
279
|
position: 'absolute',
|
|
73
280
|
inset: 0,
|
|
74
281
|
'&::before, &::after': {
|
|
75
|
-
content: '"
|
|
282
|
+
content: "''",
|
|
76
283
|
position: 'absolute',
|
|
77
284
|
width: 520,
|
|
78
285
|
height: 520,
|
|
@@ -105,210 +312,17 @@ export default function Page() {
|
|
|
105
312
|
}
|
|
106
313
|
}}
|
|
107
314
|
/>
|
|
108
|
-
|
|
109
315
|
<Container maxWidth='lg' sx={{ position: 'relative', zIndex: 1, py: { xs: 6, md: 10 } }}>
|
|
110
|
-
{
|
|
111
|
-
<Stack spacing={3} alignItems='center' textAlign='center' sx={{ mb: { xs: 6, md: 10 } }}>
|
|
112
|
-
<Stack direction='row' spacing={1} alignItems='center'>
|
|
113
|
-
<LayoutDashboard size={28} style={{ verticalAlign: 'middle' }} />
|
|
114
|
-
<Typography variant='h4' fontWeight={800} letterSpacing={0.2} sx={{ ml: 1 }}>
|
|
115
|
-
Quickstart Next
|
|
116
|
-
</Typography>
|
|
117
|
-
</Stack>
|
|
118
|
-
|
|
119
|
-
<Typography variant='h6' sx={{ maxWidth: 860, opacity: 0.9 }}>
|
|
120
|
-
A modern Next.js boilerplate powered by <b>MUI</b> with room for <b>React Query</b>, <b>React Hook Form</b>,
|
|
121
|
-
and optional <b>Tailwind v4</b>.
|
|
122
|
-
</Typography>
|
|
123
|
-
|
|
124
|
-
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} useFlexGap flexWrap='wrap'>
|
|
125
|
-
<Chip
|
|
126
|
-
icon={<Icon icon='simple-icons:mui' width={18} height={18} style={{ borderRadius: 4 }} />}
|
|
127
|
-
label='MUI'
|
|
128
|
-
color='primary'
|
|
129
|
-
variant='filled'
|
|
130
|
-
/>
|
|
131
|
-
<Chip icon={<FormInput size={18} />} label='React Hook Form' variant='outlined' />
|
|
132
|
-
<Chip
|
|
133
|
-
icon={<Icon icon='devicon:tailwindcss' width={18} height={18} />}
|
|
134
|
-
label='Tailwind v4 (optional)'
|
|
135
|
-
variant='outlined'
|
|
136
|
-
/>
|
|
137
|
-
<Chip
|
|
138
|
-
icon={<Icon icon='devicon:typescript' width={18} height={18} style={{ borderRadius: 4 }} />}
|
|
139
|
-
label='TypeScript'
|
|
140
|
-
variant='outlined'
|
|
141
|
-
/>
|
|
142
|
-
<Chip icon={<Icon icon='devicon:nextjs' width={18} height={18} />} label='App Router' variant='outlined' />
|
|
143
|
-
</Stack>
|
|
144
|
-
|
|
145
|
-
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ pt: 1 }}>
|
|
146
|
-
<Button size='large' variant='contained' component={Link} href='/sample-form'>
|
|
147
|
-
Open Sample Form
|
|
148
|
-
</Button>
|
|
149
|
-
<Button
|
|
150
|
-
size='large'
|
|
151
|
-
variant='outlined'
|
|
152
|
-
endIcon={<OpenInNewIcon />}
|
|
153
|
-
component={Link}
|
|
154
|
-
href='#'
|
|
155
|
-
target='_blank'
|
|
156
|
-
rel='noopener'
|
|
157
|
-
>
|
|
158
|
-
View Docs
|
|
159
|
-
</Button>
|
|
160
|
-
</Stack>
|
|
161
|
-
</Stack>
|
|
162
|
-
|
|
163
|
-
{/* Content */}
|
|
316
|
+
<HeroSection copied={copied} handleCopy={handleCopy} />
|
|
164
317
|
<Grid container spacing={3}>
|
|
165
318
|
<Grid size={{ xs: 12, md: 7 }}>
|
|
166
|
-
<
|
|
167
|
-
sx={{
|
|
168
|
-
backdropFilter: 'saturate(120%) blur(6px)',
|
|
169
|
-
background: 'rgba(255,255,255,0.04)',
|
|
170
|
-
border: '1px solid',
|
|
171
|
-
borderColor: 'rgba(255,255,255,0.08)'
|
|
172
|
-
}}
|
|
173
|
-
>
|
|
174
|
-
<CardContent>
|
|
175
|
-
<Typography variant='h6' fontWeight={700} gutterBottom>
|
|
176
|
-
What’s included
|
|
177
|
-
</Typography>
|
|
178
|
-
<Stack spacing={1.25} sx={{ opacity: 0.9 }}>
|
|
179
|
-
<Typography>• Next.js 15 (App Router) + TypeScript</Typography>
|
|
180
|
-
<Typography>• MUI ThemeProvider + dark-ready UI</Typography>
|
|
181
|
-
<Typography>
|
|
182
|
-
• RHF starter form at <code>/sample-form</code>
|
|
183
|
-
</Typography>
|
|
184
|
-
<Typography>• Easy opt-in Tailwind v4 (via CLI preset)</Typography>
|
|
185
|
-
</Stack>
|
|
186
|
-
|
|
187
|
-
<Divider sx={{ my: 3 }} />
|
|
188
|
-
|
|
189
|
-
<Typography variant='subtitle2' gutterBottom>
|
|
190
|
-
Scaffold via npx
|
|
191
|
-
</Typography>
|
|
192
|
-
|
|
193
|
-
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5} alignItems='center'>
|
|
194
|
-
<Code>npx @hadi87s/quickstart-next@latest</Code>
|
|
195
|
-
<Tooltip title={copied ? 'Copied!' : 'Copy'}>
|
|
196
|
-
<Button
|
|
197
|
-
variant='outlined'
|
|
198
|
-
size='small'
|
|
199
|
-
startIcon={<ContentCopyIcon fontSize='small' />}
|
|
200
|
-
onClick={() => handleCopy('npx @hadi87s/quickstart-next@latest')}
|
|
201
|
-
>
|
|
202
|
-
{copied ? 'Copied' : 'Copy'}
|
|
203
|
-
</Button>
|
|
204
|
-
</Tooltip>
|
|
205
|
-
</Stack>
|
|
206
|
-
</CardContent>
|
|
207
|
-
</Card>
|
|
319
|
+
<WhatsIncludedCard copied={copied} handleCopy={handleCopy} />
|
|
208
320
|
</Grid>
|
|
209
|
-
|
|
210
321
|
<Grid size={{ xs: 12, md: 5 }}>
|
|
211
|
-
<
|
|
212
|
-
sx={{
|
|
213
|
-
height: '100%',
|
|
214
|
-
backdropFilter: 'saturate(120%) blur(6px)',
|
|
215
|
-
background: 'rgba(255,255,255,0.04)',
|
|
216
|
-
border: '1px solid',
|
|
217
|
-
borderColor: 'rgba(255,255,255,0.08)'
|
|
218
|
-
}}
|
|
219
|
-
>
|
|
220
|
-
<CardContent>
|
|
221
|
-
<Typography variant='h6' fontWeight={700} gutterBottom>
|
|
222
|
-
Tech Logos
|
|
223
|
-
</Typography>
|
|
224
|
-
<Stack direction='row' spacing={2} alignItems='center' sx={{ pb: 1.5 }}>
|
|
225
|
-
<Tooltip title='MUI' arrow placement='top'>
|
|
226
|
-
<span>
|
|
227
|
-
<Icon icon='simple-icons:mui' width={28} height={28} style={{ borderRadius: 4 }} />
|
|
228
|
-
</span>
|
|
229
|
-
</Tooltip>
|
|
230
|
-
<Tooltip title='Tailwind CSS' arrow placement='top'>
|
|
231
|
-
<span>
|
|
232
|
-
<Icon icon='devicon:tailwindcss' width={28} height={28} />
|
|
233
|
-
</span>
|
|
234
|
-
</Tooltip>
|
|
235
|
-
<Tooltip title='React' arrow placement='top'>
|
|
236
|
-
<span>
|
|
237
|
-
<Icon icon='devicon:react' width={28} height={28} />
|
|
238
|
-
</span>
|
|
239
|
-
</Tooltip>
|
|
240
|
-
<Tooltip title='Next.js' arrow placement='top'>
|
|
241
|
-
<span>
|
|
242
|
-
<Icon icon='devicon:nextjs' width={28} height={28} />
|
|
243
|
-
</span>
|
|
244
|
-
</Tooltip>
|
|
245
|
-
<Tooltip title='React Hook Form' arrow placement='top'>
|
|
246
|
-
<span>
|
|
247
|
-
<Icon icon='simple-icons:reacthookform' width={28} height={28} />
|
|
248
|
-
</span>
|
|
249
|
-
</Tooltip>
|
|
250
|
-
<Tooltip title='TypeScript' arrow placement='top'>
|
|
251
|
-
<span>
|
|
252
|
-
<Icon icon='devicon:typescript' width={28} height={28} style={{ borderRadius: 4 }} />
|
|
253
|
-
</span>
|
|
254
|
-
</Tooltip>
|
|
255
|
-
</Stack>
|
|
256
|
-
|
|
257
|
-
<Typography variant='body2' sx={{ opacity: 0.85 }}>
|
|
258
|
-
This template ships with MUI by default. You can enable Tailwind v4 at scaffold time. React Query and
|
|
259
|
-
other integrations can be added as the stack grows.
|
|
260
|
-
</Typography>
|
|
261
|
-
|
|
262
|
-
<Divider sx={{ my: 2 }} />
|
|
263
|
-
|
|
264
|
-
<Stack direction='row' spacing={1.5}>
|
|
265
|
-
<Button
|
|
266
|
-
variant='outlined'
|
|
267
|
-
size='small'
|
|
268
|
-
startIcon={<Github size={18} />}
|
|
269
|
-
endIcon={<OpenInNewIcon />}
|
|
270
|
-
component={Link}
|
|
271
|
-
href='https://github.com/hadi87s/quickstart-next'
|
|
272
|
-
target='_blank'
|
|
273
|
-
rel='noopener'
|
|
274
|
-
>
|
|
275
|
-
GitHub
|
|
276
|
-
</Button>
|
|
277
|
-
<Button
|
|
278
|
-
variant='outlined'
|
|
279
|
-
size='small'
|
|
280
|
-
startIcon={<Package size={18} />}
|
|
281
|
-
endIcon={<OpenInNewIcon />}
|
|
282
|
-
component={Link}
|
|
283
|
-
href='https://www.npmjs.com/package/@hadi87s/quickstart-next'
|
|
284
|
-
target='_blank'
|
|
285
|
-
rel='noopener'
|
|
286
|
-
>
|
|
287
|
-
npm
|
|
288
|
-
</Button>
|
|
289
|
-
</Stack>
|
|
290
|
-
</CardContent>
|
|
291
|
-
</Card>
|
|
322
|
+
<TechLogosCard />
|
|
292
323
|
</Grid>
|
|
293
324
|
</Grid>
|
|
294
|
-
|
|
295
|
-
{/* Footer */}
|
|
296
|
-
<Stack alignItems='center' sx={{ mt: 8, opacity: 0.65 }}>
|
|
297
|
-
<Typography variant='body2'>
|
|
298
|
-
Built with ❤️ by{' '}
|
|
299
|
-
<MuiLink
|
|
300
|
-
href='https://github.com/hadi87s/quickstart-next'
|
|
301
|
-
underline='none'
|
|
302
|
-
color='primary'
|
|
303
|
-
sx={{ fontWeight: 600 }}
|
|
304
|
-
target='_blank'
|
|
305
|
-
rel='noopener'
|
|
306
|
-
>
|
|
307
|
-
Hadi
|
|
308
|
-
</MuiLink>{' '}
|
|
309
|
-
using MUI. Ready for Tailwind v4, React Query, and more.
|
|
310
|
-
</Typography>
|
|
311
|
-
</Stack>
|
|
325
|
+
<Footer />
|
|
312
326
|
</Container>
|
|
313
327
|
</Box>
|
|
314
328
|
)
|
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { PropsWithChildren, useState } from 'react'
|
|
4
|
-
import { CssBaseline, ThemeProvider, createTheme } from '@mui/material'
|
|
5
4
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
6
|
-
|
|
7
|
-
const theme = createTheme({
|
|
8
|
-
palette: { mode: 'dark' },
|
|
9
|
-
shape: { borderRadius: 12 },
|
|
10
|
-
typography: { fontFamily: 'system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif' }
|
|
11
|
-
})
|
|
5
|
+
import ThemeComponent from '@/@core/theme/ThemeComponent'
|
|
12
6
|
|
|
13
7
|
export default function BaseProviders({ children }: PropsWithChildren) {
|
|
14
8
|
const [client] = useState(() => new QueryClient())
|
|
15
9
|
return (
|
|
16
|
-
<
|
|
17
|
-
{/* If you kept Tailwind preflight OFF, keep CssBaseline ON */}
|
|
18
|
-
<CssBaseline />
|
|
10
|
+
<ThemeComponent>
|
|
19
11
|
<QueryClientProvider client={client}>{children}</QueryClientProvider>
|
|
20
|
-
</
|
|
12
|
+
</ThemeComponent>
|
|
21
13
|
)
|
|
22
14
|
}
|