소스 검색

Build Zoom-style macOS sign-in UI with sidebar and tuned proportions.

Align colors with classroom_app dark-mode palette for a true dark appearance while keeping Zoom-like blue accents.

Made-with: Cursor
huzaifahayat12 3 달 전
부모
커밋
135a5b6480
1개의 변경된 파일296개의 추가작업 그리고 4개의 파일을 삭제
  1. 296 4
      zoom_app/ViewController.swift

+ 296 - 4
zoom_app/ViewController.swift

@@ -8,19 +8,311 @@
 import Cocoa
 
 class ViewController: NSViewController {
+    private let sidebarWidth: CGFloat = 78
+    // Dark-mode values copied from classroom_app Palette(isDarkMode: true).
+    private let appBackground = NSColor(calibratedRed: 10 / 255, green: 11 / 255, blue: 12 / 255, alpha: 1)
+    private let sidebarBackground = NSColor(calibratedRed: 16 / 255, green: 17 / 255, blue: 19 / 255, alpha: 1)
+    private let sidebarActiveBackground = NSColor(calibratedRed: 22 / 255, green: 23 / 255, blue: 26 / 255, alpha: 1)
+    private let cardBackground = NSColor(calibratedRed: 20 / 255, green: 21 / 255, blue: 24 / 255, alpha: 1)
+    private let separatorColor = NSColor(calibratedRed: 26 / 255, green: 27 / 255, blue: 30 / 255, alpha: 1)
+    private let accentBlue = NSColor(calibratedRed: 56 / 255, green: 132 / 255, blue: 255 / 255, alpha: 1)
+    private let primaryText = NSColor(calibratedWhite: 0.98, alpha: 1)
+    private let secondaryText = NSColor(calibratedWhite: 0.78, alpha: 1)
+    private let mutedText = NSColor(calibratedWhite: 0.66, alpha: 1)
 
     override func viewDidLoad() {
         super.viewDidLoad()
+        setupUI()
+    }
+
+    override func viewDidAppear() {
+        super.viewDidAppear()
+
+        // Keep a practical default size for this desktop mock.
+        view.window?.setContentSize(NSSize(width: 960, height: 700))
+        view.window?.title = "zoom Workplace"
+    }
+
+    private func setupUI() {
+        view.wantsLayer = true
+        view.layer?.backgroundColor = appBackground.cgColor
+
+        let sidebar = makeSidebar()
+        let content = makeContent()
+
+        view.addSubview(sidebar)
+        view.addSubview(content)
+
+        sidebar.translatesAutoresizingMaskIntoConstraints = false
+        content.translatesAutoresizingMaskIntoConstraints = false
+
+        NSLayoutConstraint.activate([
+            sidebar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+            sidebar.topAnchor.constraint(equalTo: view.topAnchor),
+            sidebar.bottomAnchor.constraint(equalTo: view.bottomAnchor),
+            sidebar.widthAnchor.constraint(equalToConstant: sidebarWidth),
+
+            content.leadingAnchor.constraint(equalTo: sidebar.trailingAnchor),
+            content.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+            content.topAnchor.constraint(equalTo: view.topAnchor),
+            content.bottomAnchor.constraint(equalTo: view.bottomAnchor)
+        ])
+    }
+
+    private func makeSidebar() -> NSView {
+        let sidebar = NSView()
+        sidebar.wantsLayer = true
+        sidebar.layer?.backgroundColor = sidebarBackground.cgColor
+
+        let title = NSTextField(labelWithString: "Home")
+        title.font = .systemFont(ofSize: 11, weight: .medium)
+        title.textColor = primaryText
+        title.alignment = .center
+
+        let homeBox = NSBox()
+        homeBox.boxType = .custom
+        homeBox.borderType = .noBorder
+        homeBox.cornerRadius = 12
+        homeBox.fillColor = sidebarActiveBackground
+
+        let homeIcon = NSTextField(labelWithString: "⌂")
+        homeIcon.font = .systemFont(ofSize: 20, weight: .medium)
+        homeIcon.textColor = .white
+        homeIcon.alignment = .center
 
-        // Do any additional setup after loading the view.
+        homeBox.contentView?.addSubview(homeIcon)
+        homeIcon.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            homeIcon.centerXAnchor.constraint(equalTo: homeBox.contentView!.centerXAnchor),
+            homeIcon.centerYAnchor.constraint(equalTo: homeBox.contentView!.centerYAnchor)
+        ])
+
+        let sidebarItems = ["Chat", "Phone", "Docs", "Whiteboards", "Clips", "More"]
+        var itemViews: [NSView] = []
+
+        for item in sidebarItems {
+            let container = NSView()
+            let icon = NSTextField(labelWithString: "◻︎")
+            icon.font = .systemFont(ofSize: 15, weight: .regular)
+            icon.textColor = secondaryText
+            icon.alignment = .center
+
+            let label = NSTextField(labelWithString: item)
+            label.font = .systemFont(ofSize: 12, weight: .regular)
+            label.textColor = secondaryText
+            label.alignment = .center
+
+            container.addSubview(icon)
+            container.addSubview(label)
+            icon.translatesAutoresizingMaskIntoConstraints = false
+            label.translatesAutoresizingMaskIntoConstraints = false
+
+            NSLayoutConstraint.activate([
+                icon.topAnchor.constraint(equalTo: container.topAnchor),
+                icon.centerXAnchor.constraint(equalTo: container.centerXAnchor),
+                label.topAnchor.constraint(equalTo: icon.bottomAnchor, constant: 5),
+                label.centerXAnchor.constraint(equalTo: container.centerXAnchor),
+                label.bottomAnchor.constraint(equalTo: container.bottomAnchor)
+            ])
+            itemViews.append(container)
+        }
+
+        let stack = NSStackView(views: itemViews)
+        stack.orientation = .vertical
+        stack.spacing = 16
+        stack.alignment = .centerX
+        stack.distribution = .gravityAreas
+
+        sidebar.addSubview(homeBox)
+        sidebar.addSubview(title)
+        sidebar.addSubview(stack)
+
+        homeBox.translatesAutoresizingMaskIntoConstraints = false
+        title.translatesAutoresizingMaskIntoConstraints = false
+        stack.translatesAutoresizingMaskIntoConstraints = false
+
+        NSLayoutConstraint.activate([
+            homeBox.topAnchor.constraint(equalTo: sidebar.topAnchor, constant: 42),
+            homeBox.centerXAnchor.constraint(equalTo: sidebar.centerXAnchor),
+            homeBox.widthAnchor.constraint(equalToConstant: 56),
+            homeBox.heightAnchor.constraint(equalToConstant: 56),
+
+            title.topAnchor.constraint(equalTo: homeBox.bottomAnchor, constant: 8),
+            title.centerXAnchor.constraint(equalTo: sidebar.centerXAnchor),
+
+            stack.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 30),
+            stack.leadingAnchor.constraint(equalTo: sidebar.leadingAnchor),
+            stack.trailingAnchor.constraint(equalTo: sidebar.trailingAnchor)
+        ])
+
+        return sidebar
     }
 
-    override var representedObject: Any? {
-        didSet {
-        // Update the view, if already loaded.
+    private func makeContent() -> NSView {
+        let content = NSView()
+
+        let back = NSTextField(labelWithString: "‹  Back")
+        back.font = .systemFont(ofSize: 34, weight: .regular)
+        back.textColor = accentBlue
+
+        let logo = NSTextField(labelWithString: "zoom\nWorkplace")
+        logo.font = .systemFont(ofSize: 24, weight: .bold)
+        logo.textColor = primaryText
+        logo.alignment = .center
+        logo.maximumNumberOfLines = 2
+
+        let domain = NSTextField(labelWithString: "us05web.zoom.us")
+        domain.font = .systemFont(ofSize: 16, weight: .semibold)
+        domain.textColor = primaryText
+        domain.alignment = .center
+
+        let emailField = NSTextField()
+        emailField.placeholderString = "Email or phone number"
+        emailField.font = .systemFont(ofSize: 20, weight: .regular)
+        emailField.textColor = .white
+        emailField.focusRingType = .none
+        emailField.wantsLayer = true
+        emailField.layer?.cornerRadius = 10
+        emailField.layer?.borderWidth = 1.5
+        emailField.layer?.borderColor = accentBlue.cgColor
+        emailField.layer?.backgroundColor = cardBackground.cgColor
+
+        let nextButton = NSButton(title: "Next", target: nil, action: nil)
+        nextButton.font = .systemFont(ofSize: 20, weight: .semibold)
+        nextButton.bezelStyle = .regularSquare
+        nextButton.isBordered = false
+        nextButton.wantsLayer = true
+        nextButton.layer?.cornerRadius = 10
+        nextButton.layer?.backgroundColor = cardBackground.cgColor
+        nextButton.contentTintColor = mutedText
+
+        let divider = NSBox()
+        divider.boxType = .separator
+        divider.borderColor = separatorColor
+
+        let signInText = NSTextField(labelWithString: "or sign in with")
+        signInText.font = .systemFont(ofSize: 14, weight: .regular)
+        signInText.textColor = secondaryText
+        signInText.alignment = .center
+
+        let ssoButton = makeSocialButton(icon: "🔑", text: "SSO")
+        let googleButton = makeSocialButton(icon: "G", text: "Google")
+        let appleButton = makeSocialButton(icon: "", text: "Apple")
+        let facebookButton = makeSocialButton(icon: "f", text: "Facebook")
+        let microsoftButton = makeSocialButton(icon: "■", text: "Microsoft")
+
+        let socialStack = NSStackView(views: [ssoButton, googleButton, appleButton, facebookButton, microsoftButton])
+        socialStack.orientation = .horizontal
+        socialStack.spacing = 14
+        socialStack.distribution = .fillEqually
+
+        let signup = NSTextField(labelWithString: "Don't have an account? Sign up")
+        signup.font = .systemFont(ofSize: 15, weight: .regular)
+        signup.textColor = primaryText
+        signup.alignment = .center
+
+        let footer = NSTextField(labelWithString: "Help    Terms    Privacy")
+        footer.font = .systemFont(ofSize: 14, weight: .regular)
+        footer.textColor = NSColor(calibratedRed: 72 / 255, green: 129 / 255, blue: 218 / 255, alpha: 1)
+        footer.alignment = .center
+
+        content.addSubview(back)
+        content.addSubview(logo)
+        content.addSubview(domain)
+        content.addSubview(emailField)
+        content.addSubview(nextButton)
+        content.addSubview(divider)
+        content.addSubview(signInText)
+        content.addSubview(socialStack)
+        content.addSubview(signup)
+        content.addSubview(footer)
+
+        [back, logo, domain, emailField, nextButton, divider, signInText, socialStack, signup, footer].forEach {
+            $0.translatesAutoresizingMaskIntoConstraints = false
         }
+
+        NSLayoutConstraint.activate([
+            back.topAnchor.constraint(equalTo: content.topAnchor, constant: 26),
+            back.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 34),
+
+            logo.topAnchor.constraint(equalTo: content.topAnchor, constant: 118),
+            logo.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+
+            domain.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: 12),
+            domain.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+
+            emailField.topAnchor.constraint(equalTo: domain.bottomAnchor, constant: 30),
+            emailField.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+            emailField.widthAnchor.constraint(equalToConstant: 520),
+            emailField.heightAnchor.constraint(equalToConstant: 52),
+
+            nextButton.topAnchor.constraint(equalTo: emailField.bottomAnchor, constant: 20),
+            nextButton.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+            nextButton.widthAnchor.constraint(equalTo: emailField.widthAnchor),
+            nextButton.heightAnchor.constraint(equalToConstant: 52),
+
+            divider.topAnchor.constraint(equalTo: nextButton.bottomAnchor, constant: 28),
+            divider.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+            divider.widthAnchor.constraint(equalTo: emailField.widthAnchor),
+
+            signInText.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+            signInText.centerYAnchor.constraint(equalTo: divider.centerYAnchor),
+
+            socialStack.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 18),
+            socialStack.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+            socialStack.widthAnchor.constraint(equalTo: emailField.widthAnchor),
+
+            signup.topAnchor.constraint(equalTo: socialStack.bottomAnchor, constant: 14),
+            signup.centerXAnchor.constraint(equalTo: content.centerXAnchor),
+
+            footer.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -16),
+            footer.centerXAnchor.constraint(equalTo: content.centerXAnchor)
+        ])
+
+        return content
     }
 
+    private func makeSocialButton(icon: String, text: String) -> NSView {
+        let wrapper = NSView()
+        let iconLabel = NSTextField(labelWithString: icon)
+        iconLabel.font = .systemFont(ofSize: 22, weight: .medium)
+        iconLabel.alignment = .center
+        iconLabel.textColor = primaryText
+
+        let iconBox = NSBox()
+        iconBox.boxType = .custom
+        iconBox.cornerRadius = 12
+        iconBox.borderType = .noBorder
+        iconBox.fillColor = cardBackground
 
+        let textLabel = NSTextField(labelWithString: text)
+        textLabel.font = .systemFont(ofSize: 12, weight: .regular)
+        textLabel.textColor = secondaryText
+        textLabel.alignment = .center
+
+        wrapper.addSubview(iconBox)
+        wrapper.addSubview(textLabel)
+        iconBox.contentView?.addSubview(iconLabel)
+
+        iconBox.translatesAutoresizingMaskIntoConstraints = false
+        iconLabel.translatesAutoresizingMaskIntoConstraints = false
+        textLabel.translatesAutoresizingMaskIntoConstraints = false
+
+        NSLayoutConstraint.activate([
+            iconBox.topAnchor.constraint(equalTo: wrapper.topAnchor),
+            iconBox.centerXAnchor.constraint(equalTo: wrapper.centerXAnchor),
+            iconBox.widthAnchor.constraint(equalToConstant: 52),
+            iconBox.heightAnchor.constraint(equalToConstant: 52),
+
+            iconLabel.centerXAnchor.constraint(equalTo: iconBox.contentView!.centerXAnchor),
+            iconLabel.centerYAnchor.constraint(equalTo: iconBox.contentView!.centerYAnchor),
+
+            textLabel.topAnchor.constraint(equalTo: iconBox.bottomAnchor, constant: 6),
+            textLabel.centerXAnchor.constraint(equalTo: wrapper.centerXAnchor),
+            textLabel.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor)
+        ])
+
+        return wrapper
+    }
 }