Review adjustments (#11)

This commit is contained in:
Riccardo Senica
2024-01-20 03:00:31 +01:00
committed by GitHub
parent d420ceaf9f
commit 29067035e8
12 changed files with 153 additions and 92 deletions

View File

@@ -2,6 +2,7 @@
## Next up ## Next up
- Adjust card size
- Batch email (Resend: ETA early 2024) - Batch email (Resend: ETA early 2024)
- Custom url shortener for links in the newsletter - Custom url shortener for links in the newsletter
- Cron every 10 minutes: people are more likely to open the newsletter if delivered around the time when they subscribed (if cron becomes not enough, then the cost of sending all the emails might be a bigger issue) - Cron every 10 minutes: people are more likely to open the newsletter if delivered around the time when they subscribed (if cron becomes not enough, then the cost of sending all the emails might be a bigger issue)

View File

@@ -77,3 +77,80 @@
.styledH4 { .styledH4 {
@apply text-base font-medium; @apply text-base font-medium;
} }
/* Card border gradient */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #222;
}
.gradient-border {
--radius: 5px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
color: white;
background: #222;
border-radius: var(--radius);
&::after {
position: absolute;
content: '';
top: calc(-1 * var(--radius));
left: calc(-1 * var(--radius));
z-index: -1;
width: calc(100% + var(--radius) * 2);
height: calc(100% + var(--radius) * 2);
background: linear-gradient(
60deg,
hsl(224, 85%, 66%),
hsl(269, 85%, 66%),
hsl(314, 85%, 66%),
hsl(359, 85%, 66%),
hsl(44, 85%, 66%),
hsl(89, 85%, 66%),
hsl(134, 85%, 66%),
hsl(179, 85%, 66%)
);
background-size: 300% 300%;
background-position: 0 50%;
border-radius: calc(2 * var(--radius));
animation: moveGradient 4s alternate infinite;
}
}
@keyframes moveGradient {
50% {
background-position: 100% 50%;
}
}
/* Button gradient */
.btn-grad {
background-image: linear-gradient(
to right,
#da22ff 0%,
#9733ee 51%,
#da22ff 100%
);
padding: 10px;
width: 100%;
text-align: center;
text-transform: uppercase;
transition: 0.5s;
background-size: 200% auto;
color: white;
box-shadow: 0 0 20px #eee;
border-radius: 10px;
display: block;
}
.btn-grad:hover {
background-position: right center;
color: #fff;
text-decoration: none;
}

View File

@@ -30,7 +30,9 @@ export default function RootLayout({
fontSans.variable fontSans.variable
)} )}
> >
<Background>{children}</Background> <Background>
<div style={{ zIndex: 2 }}>{children}</div>
</Background>
<Analytics /> <Analytics />
</body> </body>
</html> </html>

View File

@@ -80,21 +80,27 @@ export default function Home() {
} }
return ( return (
<div className='h-12 align-top'> <div className='mx-2 h-44'>
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(handleSubmit)} onSubmit={form.handleSubmit(handleSubmit)}
className='flex space-x-4' className='flex flex-col space-y-4'
> >
<FormField <FormField
control={form.control} control={form.control}
name='email' name='email'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className='h-4'>
<FormMessage className='text-center' />
</div>
<FormControl> <FormControl>
<Input placeholder='example@example.com' {...field} /> <Input
placeholder='example@example.com'
className='text-center'
{...field}
/>
</FormControl> </FormControl>
<FormMessage />
</FormItem> </FormItem>
)} )}
/> />
@@ -103,15 +109,19 @@ export default function Home() {
</div> </div>
</form> </form>
</Form> </Form>
<p className='py-1 text-center text-xs text-gray-600'>
You can rest assured that we will fill your inbox with spam. We
don&apos;t like it either! 🙂
</p>
</div> </div>
); );
} }
return ( return (
<Card <Card
style='text-center' style='text-center max-w-96'
title='Hackernews + newsletter' title='Interested in keeping up with the latest from the tech world? 👩‍💻'
description='Top stories from Hackernews. Once a day. Every day.' description='Subscribe to our newsletter! The top stories from Hackernews for you. Once a day. Every day.'
content={render()} content={render()}
/> />
); );

View File

@@ -3,7 +3,7 @@ import { Card } from '../../components/custom/card';
export default function Privacy() { export default function Privacy() {
const body = ( const body = (
<div> <div className='my-2 max-h-[60vh] overflow-auto'>
<p> <p>
This Privacy Policy describes Our policies and procedures on the This Privacy Policy describes Our policies and procedures on the
collection, use and disclosure of Your information when You use the collection, use and disclosure of Your information when You use the
@@ -430,10 +430,10 @@ export default function Privacy() {
return ( return (
<Card <Card
style='max-h-[90vh] max-w-[90vw]'
title='Privacy Policy' title='Privacy Policy'
description='Last updated: December 03, 2023' description='Last updated: December 03, 2023'
content={body} content={body}
style='w-2/3 h-[90vh]'
/> />
); );
} }

View File

@@ -80,21 +80,23 @@ export default function Unsubscribe() {
} }
return ( return (
<div className='h-12 align-top'> <div className='mb-5 h-32'>
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(handleSubmit)} onSubmit={form.handleSubmit(handleSubmit)}
className='flex space-x-4' className='flex flex-col space-y-4'
> >
<FormField <FormField
control={form.control} control={form.control}
name='email' name='email'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className='h-4'>
<FormMessage className='text-center' />
</div>
<FormControl> <FormControl>
<Input placeholder='example@example.com' {...field} /> <Input placeholder='example@example.com' {...field} />
</FormControl> </FormControl>
<FormMessage />
</FormItem> </FormItem>
)} )}
/> />
@@ -109,7 +111,7 @@ export default function Unsubscribe() {
return ( return (
<Card <Card
style='text-center' style='text-center max-w-80'
title='Unsubscribe' title='Unsubscribe'
description='You sure you want to leave? :(' description='You sure you want to leave? :('
content={render()} content={render()}

View File

@@ -36,45 +36,34 @@ export const Card = ({
return null; return null;
} }
if (isMobile) {
console.log(isMobile);
return (
<CardUI className={`max-h-[90vh] w-[90%] p-4`}>
<CardHeader className='text-center'>
<CardTitle>{title}</CardTitle>
<CardDescription>{description}</CardDescription>
</CardHeader>
<CardContent className='max-h-[60vh] overflow-auto'>
{content}
</CardContent>
{footer && (
<CardFooter className='flex justify-center p-4'>
<Footer />
</CardFooter>
)}
</CardUI>
);
}
return ( return (
<div className='gradient-border'>
<CardUI <CardUI
style={{ style={{
boxShadow: '0 16px 32px 0 rgba(0, 0, 0, 0.6)' boxShadow: '0 16px 32px 0 rgba(0, 0, 0, 0.6)'
}} }}
className={`${style ?? 'sm:w-2/3 md:w-2/5 lg:w-1/3 xl:w-1/4'} p-4`} className={`max-h-[90vh] w-[90vw] p-8 ${style}`}
> >
<CardHeader className='text-center'> <CardHeader>
<p className='text-xs uppercase text-gray-500'>
Hackernews + newsletter
</p>
<CardTitle>{title}</CardTitle> <CardTitle>{title}</CardTitle>
<CardDescription>{description}</CardDescription> <CardDescription>{description}</CardDescription>
</CardHeader> </CardHeader>
<CardContent className='h-[80%] flex-grow overflow-auto'> {isMobile ? (
<CardContent>{content}</CardContent>
) : (
<CardContent className='flex max-h-[60vh] flex-grow justify-center overflow-auto'>
{content} {content}
</CardContent> </CardContent>
)}
{footer && ( {footer && (
<CardFooter className=' flex justify-center p-4'> <CardFooter>
<Footer /> <Footer />
</CardFooter> </CardFooter>
)} )}
</CardUI> </CardUI>
</div>
); );
}; };

View File

@@ -1,55 +1,19 @@
import { Slot } from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react'; import * as React from 'react';
import { cn } from '../../utils/ui'; import { cn } from '../../utils/ui';
const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default:
'bg-primary text-primary-foreground hover:bg-primary/90 bg-blue-500 hover:bg-blue-700',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline'
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10'
}
},
defaultVariants: {
variant: 'default',
size: 'default'
}
}
);
export type ButtonProps = { export type ButtonProps = {
asChild?: boolean; asChild?: boolean;
} & React.ButtonHTMLAttributes<HTMLButtonElement> & } & React.ButtonHTMLAttributes<HTMLButtonElement>;
VariantProps<typeof buttonVariants>;
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => { ({ asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button'; const Comp = asChild ? Slot : 'button';
return ( return (
<Comp <Comp className={cn('btn-grad', 'btn-grad-hover')} ref={ref} {...props} />
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
); );
} }
); );
Button.displayName = 'Button'; Button.displayName = 'Button';
export { Button, buttonVariants }; export { Button };

View File

@@ -22,7 +22,7 @@ const CardHeader = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn('flex flex-col space-y-1.5 p-6', className)} className={cn('flex flex-col space-y-1.5 text-center', className)}
{...props} {...props}
/> />
)); ));
@@ -59,7 +59,7 @@ const CardContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div ref={ref} className={cn('px-8 py-4', className)} {...props} /> <div ref={ref} className={cn('py-4', className)} {...props} />
)); ));
CardContent.displayName = 'CardContent'; CardContent.displayName = 'CardContent';
@@ -69,7 +69,7 @@ const CardFooter = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn('flex items-center p-8', className)} className={cn('flex items-center justify-center', className)}
{...props} {...props}
/> />
)); ));

View File

@@ -27,6 +27,7 @@
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"next": "^14.1.0", "next": "^14.1.0",
"postcss-nesting": "^12.0.2",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"react-hook-form": "^7.48.2", "react-hook-form": "^7.48.2",

View File

@@ -1,6 +1,8 @@
module.exports = { module.exports = {
plugins: { plugins: {
'postcss-import': {},
'tailwindcss/nesting': 'postcss-nesting',
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {}
}, }
}; };

View File

@@ -198,6 +198,11 @@
dependencies: dependencies:
chalk "^4.1.0" chalk "^4.1.0"
"@csstools/selector-specificity@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.0.1.tgz#d84597fbc0f897240c12fc0a31e492b036c70e40"
integrity sha512-NPljRHkq4a14YzZ3YD406uaxh7s0g6eAq3L9aLOWywoqe8PkYamAvtsh7KNX6c++ihDrJ0RiU+/z7rGnhlZ5ww==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -3189,7 +3194,15 @@ postcss-nested@^6.0.1:
dependencies: dependencies:
postcss-selector-parser "^6.0.11" postcss-selector-parser "^6.0.11"
postcss-selector-parser@^6.0.11: postcss-nesting@^12.0.2:
version "12.0.2"
resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-12.0.2.tgz#cb92061347db3e7c38c174c97cb01feff2eef439"
integrity sha512-63PpJHSeNs93S3ZUIyi+7kKx4JqOIEJ6QYtG3x+0qA4J03+4n0iwsyA1GAHyWxsHYljQS4/4ZK1o2sMi70b5wQ==
dependencies:
"@csstools/selector-specificity" "^3.0.1"
postcss-selector-parser "^6.0.13"
postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.13:
version "6.0.15" version "6.0.15"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535"
integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==