diff --git a/resources/data.edn b/resources/data.edn index d53ead5..e746313 100644 --- a/resources/data.edn +++ b/resources/data.edn @@ -75,6 +75,7 @@ :url "https://codepen.io/user/experiment" :technologies ["JavaScript" "Canvas API"]}] - :social {:github "https://github.com/username" - :linkedin "https://linkedin.com/in/username" - :twitter "https://twitter.com/username"}} + :social {:email "joseph@ferano.io" + :github "https://github.com/JosephFerano" + :gitea "https://git.ferano.io/JosephFerano?tab=activity" + :linkedin "https://linkedin.com/in/josephferano"}} diff --git a/src/ferano_io/core.clj b/src/ferano_io/core.clj index abd41a7..52ee865 100644 --- a/src/ferano_io/core.clj +++ b/src/ferano_io/core.clj @@ -4,15 +4,22 @@ [clojure.edn :as edn])) (defn header [data] - [:header {:class "sticky top-0 bg-white shadow-sm z-50"} - [:div {:class "max-w-6xl mx-auto px-4 py-4 flex justify-between items-center"} - [:h1 {:class "text-2xl font-bold"} "Joseph Ferano"] - [:nav {:class "hidden md:block"} - [:ul {:class "flex space-x-8"} - (for [[id label] (:sections data)] - [:li [:a {:href (str "#" (name id)) :class "hover:text-blue-600 transition-colors"} label]])]] - ;; Mobile hamburger (add JS later) - [:button {:class "md:hidden p-2"} "☰"]]]) + [:header {:class "sticky top-0 bg-white shadow-sm z-50"} + [:div {:class "max-w-6xl mx-auto px-4 py-4 flex justify-between items-center"} + [:h1 {:class "text-2xl font-bold"} "Joseph Ferano"] + [:nav {:class "hidden md:block"} + [:ul {:class "flex space-x-8"} + (for [[id label] (:sections data)] + [:li [:a {:href (str "#" (name id)) :class "hover:text-blue-600 transition-colors"} label]])]] + [:div {:class "md:hidden"} + [:input {:type "checkbox" :id "menu-toggle" :class "menu-toggle"}] + [:label {:for "menu-toggle" :class "p-2 cursor-pointer"} "☰"] + [:nav {:class "mobile-menu absolute top-full left-0 w-full bg-white shadow-lg"} + [:ul {:class "flex flex-row p-4 space-x-4 justify-center"} + (for [[id label] (:sections data)] + [:li [:a {:href (str "#" (name id)) :class "py-2 hover:text-blue-600 text-xs" + :onclick "document.getElementById('menu-toggle').checked = false;"} + label]])]]]]]) (defn hero [data] [:section {:id "hero" :class "bg-gray-900 py-8 md:py-16"} @@ -27,27 +34,28 @@ "Building engaging experiences and robust systems across gaming, graphics, blockchain, and web technologies. Multilingual programmer with a diverse technical toolkit for solving complex problems."]]]]]) (defn game-card [game] - [:div {:class "bg-gray-50 border border-gray-200 p-5 flex flex-col md:flex-row gap-4 md:gap-8 md:items-center hover:shadow-md transition-shadow cursor-pointer"} - [:div {:class "flex-shrink-0 w-full md:w-80"} + [:div {:class "bg-white border border-gray-200 p-5 hover:shadow-md transition-shadow"} + [:div {:class "w-full mb-4"} [:img {:src (:image game) :alt (:name game) - :class "w-full h-48 md:h-full object-cover"}]] - [:div {:class "flex-1 flex flex-col gap-4"} - [:h3 {:class "text-xl font-normal"} (:name game)] - [:p {:class "text-sm text-gray-600 leading-relaxed"} (:description game)] - [:div {:class "text-xs text-gray-500 italic"} - (clojure.string/join ", " (:technologies game))] - [:div {:class "flex justify-center gap-4"} - (for [link (:links game)] - [:a {:href (:url link) - :target "_blank" - :class "text-xs text-gray-800 no-underline py-1 px-3 border border-gray-300 hover:bg-gray-100 transition-colors"} - (:title link)])]]]) + :class "w-full h-48 object-cover"}]] + [:div {:class "flex justify-between items-start mb-3"} + [:h3 {:class "font-medium text-lg"} (:name game)]] + [:p {:class "text-sm text-gray-600 mb-4 line-clamp-3"} (:description game)] + [:div {:class "flex flex-wrap gap-1 mb-4"} + (for [tech (:technologies game)] + [:span {:class "text-xs px-2 py-1 bg-gray-100 rounded"} tech])] + [:div {:class "flex gap-3"} + (for [link (:links game)] + [:a {:href (:url link) + :target "_blank" + :class "text-xs text-blue-600 hover:underline"} + (:title link)])]]) (defn games [data] - [:section {:id "work" :class "py-5 pb-15"} + [:section {:id "work" :class "py-16"} [:h2 {:class "text-lg tracking-wide mb-8 text-center"} "FEATURED GAMES"] - [:div {:class "grid grid-cols-1 gap-10 max-w-4xl mx-auto"} + [:div {:class "max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-6"} (map game-card (:games data))]]) (defn projects-grid [data] @@ -70,19 +78,44 @@ [:a {:href (:demo project) :target "_blank" :class "text-xs text-blue-600 hover:underline"} "Demo"])] [:span {:class "text-xs px-2 py-1 bg-gray-100 rounded"} (:status project)]]])]]) +(defn contact [data] + [:section {:id "contact" :class "py-16 bg-gray-100"} + [:div {:class "max-w-4xl mx-auto px-4 text-center"} + [:h2 {:class "text-lg tracking-wide mb-8"} "CONTACT"] + [:p {:class "text-gray-600 mb-8"} + "Feel free to contact me"] + [:div {:class "flex justify-center gap-8 flex-wrap"} + [:a {:href "mailto:joseph@ferano.io" + :class "flex items-center gap-2 text-gray-700 hover:text-blue-600 transition-colors"} + [:span "📧"] [:span "joseph at ferano.io"]] + [:a {:href (get-in data [:social :linkedin]) + :target "_blank" + :class "flex items-center gap-2 text-gray-700 hover:text-blue-600 transition-colors"} + [:span "💼"] [:span "LinkedIn"]] + [:a {:href (get-in data [:social :gitea]) + :target "_blank" + :class "flex items-center gap-2 text-gray-700 hover:text-blue-600 transition-colors"} + [:span "🔧"] [:span "Gitea"]] + [:a {:href (get-in data [:social :github]) + :target "_blank" + :class "flex items-center gap-2 text-gray-700 hover:text-blue-600 transition-colors"} + [:span "🐙"] [:span "GitHub"]]]]]) + (defn main-page [data] (page/html5 [:head [:meta {:charset "utf-8"}] [:meta {:name "viewport" :content "width=device-width, initial-scale=1"}] [:title "Joseph Ferano"] - [:script {:src "https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"}]] + [:script {:src "https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"}] + [:style ".menu-toggle:checked + label + .mobile-menu { opacity: 1; visibility: visible; transform: translateY(0); } .menu-toggle { display: none; } .mobile-menu { opacity: 0; visibility: hidden; transform: translateY(-10px); transition: opacity 0.2s ease, visibility 0.2s ease, transform 0.2s ease; } .mobile-menu a { display: block; }"]] [:body [:div {:class "min-h-screen bg-gray-50"} (header data) (hero data) (projects-grid data) - (games data)]])) + (games data) + (contact data)]])) (defn -main [& args] (let [data (-> "resources/data.edn"