diff --git a/README.md b/README.md index 942b259..3d4ecca 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ## Next up +- Adjust card size - Batch email (Resend: ETA early 2024) - 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) diff --git a/app/globals.css b/app/globals.css index c0a1ebd..38314ba 100644 --- a/app/globals.css +++ b/app/globals.css @@ -77,3 +77,80 @@ .styledH4 { @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; +} diff --git a/app/layout.tsx b/app/layout.tsx index 2c9b1dd..f6261e7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -30,7 +30,9 @@ export default function RootLayout({ fontSans.variable )} > - {children} + +
{children}
+
diff --git a/app/page.tsx b/app/page.tsx index 3d18825..73b0edd 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -80,21 +80,27 @@ export default function Home() { } return ( -
+
( +
+ +
- + -
)} /> @@ -103,15 +109,19 @@ export default function Home() {
+

+ You can rest assured that we will fill your inbox with spam. We + don't like it either! 🙂 +

); } return ( ); diff --git a/app/privacy/page.tsx b/app/privacy/page.tsx index 806d640..5139c8a 100644 --- a/app/privacy/page.tsx +++ b/app/privacy/page.tsx @@ -3,7 +3,7 @@ import { Card } from '../../components/custom/card'; export default function Privacy() { const body = ( -
+

This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the @@ -430,10 +430,10 @@ export default function Privacy() { return ( ); } diff --git a/app/unsubscribe/page.tsx b/app/unsubscribe/page.tsx index 5b9b7c5..c50af52 100644 --- a/app/unsubscribe/page.tsx +++ b/app/unsubscribe/page.tsx @@ -80,21 +80,23 @@ export default function Unsubscribe() { } return ( -

+
( +
+ +
-
)} /> @@ -109,7 +111,7 @@ export default function Unsubscribe() { return ( - + return ( +
+ + +

+ Hackernews + newsletter +

{title} {description}
- - {content} - + {isMobile ? ( + {content} + ) : ( + + {content} + + )} {footer && ( - +
)} - ); - } - - return ( - - - {title} - {description} - - - {content} - - {footer && ( - -
- - )} - +
); }; diff --git a/components/ui/button.tsx b/components/ui/button.tsx index 443d3f4..0999502 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -1,55 +1,19 @@ import { Slot } from '@radix-ui/react-slot'; -import { cva, type VariantProps } from 'class-variance-authority'; import * as React from 'react'; 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 = { asChild?: boolean; -} & React.ButtonHTMLAttributes & - VariantProps; +} & React.ButtonHTMLAttributes; const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { + ({ asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : 'button'; return ( - + ); } ); Button.displayName = 'Button'; -export { Button, buttonVariants }; +export { Button }; diff --git a/components/ui/card.tsx b/components/ui/card.tsx index 083a329..cce8606 100644 --- a/components/ui/card.tsx +++ b/components/ui/card.tsx @@ -22,7 +22,7 @@ const CardHeader = React.forwardRef< >(({ className, ...props }, ref) => (
)); @@ -59,7 +59,7 @@ const CardContent = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)); CardContent.displayName = 'CardContent'; @@ -69,7 +69,7 @@ const CardFooter = React.forwardRef< >(({ className, ...props }, ref) => (
)); diff --git a/package.json b/package.json index 8997afb..dabead2 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "next": "^14.1.0", + "postcss-nesting": "^12.0.2", "react": "^18", "react-dom": "^18", "react-hook-form": "^7.48.2", diff --git a/postcss.config.js b/postcss.config.js index 12a703d..a31af1c 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,8 @@ module.exports = { plugins: { + 'postcss-import': {}, + 'tailwindcss/nesting': 'postcss-nesting', tailwindcss: {}, - autoprefixer: {}, - }, + autoprefixer: {} + } }; diff --git a/yarn.lock b/yarn.lock index e94f6eb..537bda7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -198,6 +198,11 @@ dependencies: 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": version "4.4.0" 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: 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" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==