cho.sh
2,244 notes
Liked on XI made a mistake. When Jack and I decided to organize this $100K Hackathon, my biggest concern was cheating. Unlike a regular hackathon, everyone participates from home. And because the winner is whoever makes the most PROFIT (not revenue), there are so many ways to manipulate the numbers. So I added a $99 entry fee. One thing I’ve learned from building businesses is that people tend to be more committed when they pay for something. I thought it would filter out spam, attract serious builders, and make the competition fairer for everyone. But I completely missed the other side. 3 years ago, I was a living with my apps. Today, I’m financially free. My life changed so much that somewhere along the way, I lost perspective on what $99 means to someone else. For someone in India, someone struggling to pay their bills, or someone trying to make their first dollar online, $99 can be a lot of money. And those are exactly the people I want to help! I’ve spent years sharing what I’ve learned, reviewing random people’s landing pages, and organizing free meetups around the world. Not because there’s money to make, but because I genuinely love seeing people build things and succeed. And somehow, I was so focused on keeping cheaters out that I didn’t realize I was also keeping genuine builders out. That’s my mistake. So I’ve decided to make the $100K Hackathon completely FREE :) Everyone who already paid got a full refund. I’m still worried about cheating. That problem hasn’t disappeared, and Jack and I will have to figure out better ways to verify profits and keep the leaderboard fair. But that’s our problem to solve. Thank you to everyone who shared their thoughts. Sometimes you need people to remind you of things you’ve forgotten. I genuinely hope this hackathon changes someone’s life. ❤️Liked on XIt’s time to bring back Cafe CursorPosted on Xyo @pendev i can't live share edit a .pen file? it's the most needed featurePosted on XI'm coining a word "Agentsplaining" Agents condescendingly assuming the user don't know fuck and repeating the obviousReplied on X@jayhxmo @raycast Hard to explain. It's the small detailsPosted on X@raycast iOS keyboard is insanely good. Might resub Raycast pro just cuz of thisReplied on X@ishuagra02 I saw a theory that F5.2 is a post train of 5.1, but O5.5 is a distillation of F5.5(unreleased new pretrain) and thats why O5.5 > F5.2Liked on X@anaclumos don't know what is actually improved 👀Posted on Xam i the only one underimpressed with 5.2 fableReplied on X@bvvst great minds think alike https://t.co/YRFWhb1mcOLiked on XClaude Fable 5.2 is out and it's the perfect time to run this god-level prompt on your codebase: "I want you to audit our codebase, is anything here unnecessary, overly complicated in our current setup, or based on weak assumptions? Challenge them. - What can be deleted entirely? - What can be simplified now that unnecessary pieces are gone? Then make the changes. Prefer deleting over simplifying, simplifying over optimizing, and optimizing over automating. This product is being used by thousands of customers, do not cut any corners." This prompt is one I run often and leads to the best results.Replied on X@akshayadinesh19 @bot can we please BYOM for botsLiked on XTo be fair, the Chinese models have a much lower refusal rateLiked on XF*CK IT, im creating a library of AI slop indicators. my feed is full of taste larpers. stay tuned https://t.co/5Muv8nQRm3Liked on XJENSEN! THATS NOT WHAT WE MEANT BY CHINESE MODELS!!!!!!!Liked on Xmy parents had to declare bankruptcy when I was a kid, so they had every reason to pressure me towards a more stable career path instead, they were so patient with me, even as i failed startup after startup for years so getting to tell my mom that everything turned out okay was a really special moment :) for those who can't read Korean, her first message roughly translates to: "Really!!! Congratulations my son. So much :') Thank you God!" i know I've been posting about my exit for like a week now, but it really is a big deal for me and my family! i'll share more about how this all happened and some tactical advice later this week, but for now i'm just riding out all the emotions to quote my mom, thank you God!!!!Liked on X@thefarmers_Dog it stands for Egg GzampleLiked on X🦍 Introducing Memorilla: memory as a modality for LLMs Memorilla turns everything a frozen LLM needs to remember into just 16 memory embeddings ⚡ It uses up to 4,212× fewer tokens and is 27× faster than full context inference while more accurate 🎯 Pushes the Pareto frontier over RAG and agentic memory in personalization, long-document and knowledge QA A thread 🧵:Liked on XThere's a huge product opportunity downstream of Tailscale etc too. At @tldraw we've started designing for "high trust networks" and it's been a beautiful thing. 1. Features that would be a nightmare on the internet are okay on my computer and mostly okay on private networks. Websites that execute user-generated scripts, control production systems, allow for users to send desktop notifications, spend tokens, or directly prompt my agent. Crazy stuff that would only make sense if everyone was in a committed polycule. 2. When everyone on the server can be trusted to have _external_ reasons for good behavior (eg keeping their job, or maintaining friends, family, church, mafia org), the software can be designed in a default social way. Most apps default to isolation, low horizontal visibility, highly restricted collaboration. Makes sense if the social contracts are inside of the app, but if you can trust that they live outside of the app then you can get wild. Example being small group chat names as an emergent comedy phenomenon because anyone in the group can change the name. 3. This can work for commercial products, too, not just internal stuff. Security matters more than ever but the way we've seen that manifest is that more companies are more confident in their own solutions than anything we could provide, to the point where owning data and access is a better value proposition than having it managed. Always has been true for big companies but now even small companies can get a good secure network set up pretty easily, and the hooks we need to build are easy enough. tldraw offline got us started down this road (and shout out to @pendev for first introducing me to the idea of desktop + mcp). We've been bringing that product to private networks and it's been exciting to see how much we can get away with. Keep an eye outReplied on X@saradu birds eye viewLiked on XApplications close in 2 days!! If you have a cool bio idea, and $5-10k will go a long way for you, please apply!!! https://t.co/NCvmrNC53cLiked on XWe've been early adopters of Rust since 2018 and a lot of our infra, voice tech, and even some of the current client is already Rust, the holy grail was getting the whole client there. That means Electron goes away, and so does React Native, mobile keeps native UI but shares app code with desktop via Rust. We hope to get it into testing with users soon™. (And it's not Tauri, it's GPUI, so good guess everyone.) Alright, back to staring at the WoW Forever login screen.Reposted on XOh boy. https://t.co/X7SSskGuGvLiked on XRIP .si domain squatters
Tailwind CSS 톺아보기

Tailwind CSS 톺아보기

Warning

Over a year old. May be outdated

프로젝트 목표

  • Tailwind CSS를 임의의 TypeScript Next 프로젝트에 적용
  • Tailwind CSS 디자인 패턴에 대한 기초적인 이해
  • Tailwind CSS 디자인 시스템에 대한 기초적인 이해

완성본 미리 보기

원본 자료

Documentation - Tailwind CSS

#0 프로젝트 설정

  • 기초적인 TypeScript Next App 설정
bash
 yarn create next-app --typescript
  • ./styles/* 삭제
  • pages/_app.tsx CSS 관련 코드 삭제 (또는 아래와 같이 입력)
ts
import type { AppProps } from 'next/app'

const App = ({ Component, pageProps }: AppProps) => <Component {...pageProps} />

export default App
  • pages/index.tsx CSS 관련 코드 삭제 (또는 아래와 같이 입력)
ts
import Head from 'next/head'

const index = () => (
  <>
    <Head>
      <title>Create Next App</title>
      <meta name="description" content="Generated by create next app" />
      <link rel="icon" href="/favicon.ico" />
    </Head>
    <h1>H1 Title</h1>
  </>
)

export default index

#1 Tailwind를 Next에 추가

  • 필수 요소 설치
bash
yarn add -D tailwindcss@latest postcss@latest autoprefixer@latest
  • 다음 명령으로 tailwind.config.js와 postcss.config.js 생성
bash
yarn tailwindcss init -p
  • 사용하지 않은 Style 코드에 대한 삭제 옵션 추가
js
module.exports = {
  purge: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
  • 최종적으로 Tailwind CSS를 pages/_app.tsx에 추가
ts
import type { AppProps } from 'next/app'
import 'tailwindcss/tailwind.css'

const App = ({ Component, pageProps }: AppProps) => <Component {...pageProps} />

export default App

#2 디자인 코드 분석

1. Chat Bubble

https://design-cho-sh.vercel.app/tailwind-docs/chatbubble

ts
import Image from 'next/image'
import Favicon from '../../public/favicon.ico'

const chatbubble = () => (
  <div className="flex h-screen">
    <div className="p-6 max-w-sm m-auto bg-white rounded-xl shadow-md flex items-center space-x-4 border-2">
      <div className="shrink-0 relative">
        <div className="h-12 w-12">
          <Image layout="fill" src={Favicon} alt="Favicon" />
        </div>
      </div>
      <div>
        <div>
          <div className="text-xl font-medium text-black">Chat Bubble</div>
          <p className="text-gray-500">You have a message.




        </div>
      </div>
    </div>
  </div>
)

export default chatbubble

원본 코드와 변경점

  • class 대신 className 사용 (React)
  • next/image 사용. next/image는 여전히 HTML <img>를 생성하지만 이미지 최적화 등에서 강점을 지님. 다만 Tailwind 자체의 h-12, w-12 등의 className이 지원되지 않아 Wrapper Div가 필요함.
ts
- <img class="h-12 w-12" src="../../public/favicon.ico" alt="Favicon">
+ <div className='h-12 w-12'>
+   <Image layout='fill' src={Favicon} alt='Favicon' />
+ </div>
  • Wrapper Div 추가로 화면에 중앙 정렬
ts
<div className='flex h-screen'>
-  <div className='mx-auto'>
+  <div className='m-auto'>
</div>
  • border-2로 2px 테두리 추가

키워드 분석

  • flex: display flex
  • h-screen: 컴포넌트 높이를 화면에 맞춰줌 ( height: 100vh; )
  • p-6: 컴포넌트 padding 1.5rem (24px)
  • max-w-sm: 컴포넌트 최대 너비 24rem (384px)
  • m-auto: margin-auto. Wrapper Div의 flex h-screen과 함께 컴포넌트를 중앙 정렬. mx-auto는 좌우로 margin-auto, my-auto는 상하로 margin-auto.
  • bg-white: background white
  • rounded-xl: 컴포넌트 border-radius를 0.75rem로 설정 (12px)
  • shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1) 중간 사이즈 그림자 적용. 이들은 각각 offset-x | offset-y | blur-radius | spread-radius | color를 뜻함. 참고
  • items-center: align-items: center
  • space-x-4: 컴포넌트 사이의 좌우 간격 설정
  • border-2: border 2px
  • shrink-0: 컴포넌트를 shrink하거나 wrap하지 않음.
  • relative: position relative
  • h-12, w-12: height, width를 3rem (48px)로 설정
  • layout: fill: next/image에서 이미지를 상위 컴포넌트에 맞게 Stretch. Stretch하지 않으려면 layout: responsive 사용.
  • text-xl: 폰트 사이즈 1.25rem (20px) 그리고 line-height 1.75rem (28px)
  • font-medium font-weight 500

2. Case Study Card

ts
import Image from 'next/image'

const CaseStudyCard = () => (
  <div className="flex h-screen">
    <div className="max-w-md m-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl border-2">
      <div className="md:flex">
        <div className="md:shrink-0">
          <div className="h-48 object-cover md:h-full md:w-48 relative">
            <Image src="https://cataas.com/cat" alt="Man looking at item at a store" layout="fill" objectFit="cover" />
          </div>
        </div>
        <div className="p-8">
          <div className="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Case study</div>
          <a href="#" className="block mt-1 text-lg leading-tight font-medium text-black hover:underline">
            Finding customers for your new business
          </a>
          <p className="mt-2 text-gray-500">
            Getting a new business off the ground is a lot of hard work. Here are five ideas you can use to find your
            first customers.





        </div>
      </div>
    </div>
  </div>
)

export default CaseStudyCard

원본 코드와 변경점

  • 위와 동일
  • next/image에서 layout='fill'과 objectFit='cover'을 동시에 사용하면 상위 컴포넌트에 가득 차도록 확대되며( layout='fill' ) 사진을 stretch하지 않고 넘치는 부분을 잘라낸다 ( objectFit='cover' )

키워드 분석

  • md:flex: 미디어쿼리로 @media (min-width: 768px)가 넘어가는 순간 display: flex가 적용됨.
  • 나머지는 위와 유사

3. User Email Form

ts
const UserEmailForm = () => {
  const signupUser = async (event: React.FormEvent) => {
    event.preventDefault()
  }
  return (
    <div className="flex h-screen p-6 bg-green-100">
      <div className="m-auto space-y-3">
        <div className="w-full px-6 py-5 mx-auto space-y-1 overflow-hidden transition duration-500 transform border-2 border-green-500 border-opacity-25 rounded-lg cursor-pointer select-none hover:border-2 group hover:shadow-lg motion-reduce:transform-none hover:scale-105">
          <p className="text-lg font-semibold text-green-600">New Project




          <p className="text-green-500">Create a new project from a variety of starting templates.




        </div>
        <form className="flex w-full m-auto mx-auto space-x-3" onSubmit={signupUser}>
          <input
            className="flex-1 w-full px-4 py-2 text-base text-gray-700 placeholder-gray-400 transition duration-500 transform bg-white border-2 border-green-500 border-opacity-25 rounded-lg appearance-none hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-green-600 focus:border-transparent motion-reduce:transform-none hover:scale-105"
            placeholder="Your Email"
            type="email"
          />
          <button
            className="shrink-0 px-4 py-2 text-base font-semibold text-white transition duration-500 transform bg-green-600 rounded-lg hover:shadow-lg focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 focus:ring-offset-green-200 motion-reduce:transform-none hover:scale-105 tramsform"
            type="button"
          >
            Sign up
          </button>
        </form>
      </div>
    </div>
  )
}

export default UserEmailForm

원본 코드와 변경점

  • 위와 동일
  • Hover & Focus 디자인 변화
  • Transition & Transform 사용
  • Headwind 사용

키워드 분석

  • cursor-pointer
  • motion-reduce:transform-none: 사용자의 디바이스가 Reduce Motion으로 설정된 경우 Transform 동작들을 실행하지 않음
  • transition: 기본적으로 background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter에 150ms의 cubic-bezier(0.4, 0, 0.2, 1) transition을 걸음
  • duration-500: transition-duration: 500ms;으로 변경함
  • transform
  • cursor-pointer
  • hover:scale-105 & hover:shadow-lg: 개인적으로 가장 마음에 드는 조합. 컴포넌트가 공중으로 떠오르는 느낌을 준다. 직접 테스트

분석

개인적으로 매우 마음에 들었다. 이전에는 TypeScript Next에 styled-component를 집중적으로 사용했었는데 그에 비한 Tailwind의 장점으로 다음 느낌이 들었다.

(개인적인 분석으로, 개발자 혹은 기업체의 코딩 스타일과 컨벤션에 따라 크게 달라질 수 있음.)

  1. 클래스 네이밍에 신경을 쓰지 않음. styled-component의 경우 StyledContainer, StyledLink, StyledContent 등의 이름을 반복해서 작성했었다.
  2. 짧아진 코드. 코드가 위아래로 길어지는 것을 방지한다. styled-component의 경우 백틱(`)으로 CSS 코드를 감싼 후 CSS 코드를 줄바꿈하여 작성하기 때문에 코드가 길어지곤 했었다.
  3. 스타일의 변경이 비교적 안전. 스타일을 수정해도 컴포넌트의 className을 변경하는 것이기 때문에 다른 컴포넌트의 디자인이 바뀔 일이 없다.
  4. 낮은 러닝커브. 예전에는 Tailwind의 className 속성 이름을 일일이 외워야 하는 줄 알고 러닝커브가 높을 줄 알았는데 대부분의 className이 CSS 속성들로 네이밍되어 있을 뿐만 아니라 확장 프로그램을 사용하여 자동완성을 사용하면 됐다.
  5. 완성도 높은 기본 색상 템플릿. font-weight를 고르듯이, 기본 색상 템플릿만으로도 충분히 완성도 높은 디자인이 완성된다.

그에 반해서 다음과 같은 단점이 있었다.

  1. Style code를 간편하게 재활용하고 싶은 때가 있다. 예를 들어 styled-component로 StyledButton를 만든 후 하나의 tsx 페이지 안에서 재활용하고 싶은 경우가 있는데 Tailwind의 기본적인 사용법으로는 Style code를 빠르게 재활용할 수 없다. 물론 이 문제를 해결하기 위해서는 Template Literal로 코드를 분리해서 사용하면 된다.
  2. 코드 className의 순서가 섞인다. 이 문제는 VS Code를 사용할 경우 Headwind라는 확장 프로그램으로 해결할 수 있다.
  3. 코드가 옆으로 길어지고 가독성이 떨어짐. 이 문제가 특히 신경 쓰였다. 물론 다음과 같이 강제로 접을 수는 있겠으나 조금 번거로웠다. 이 문제를 해결할 방법을 알아보고 있다.
text
className={`w-full px-6 py-5 mx-auto space-y-1 overflow-hidden
transition duration-500 transform border-2 border-green-500
border-opacity-25 rounded-lg cursor-pointer select-none hover:border-2
group hover:shadow-lg motion-reduce:transform-none hover:scale-105`}
Backlinks1