Repository: fp-matsuri/2025.fp-matsuri.org
Pull request #60: feat: プラチナスポンサーを追加し、heroSectionのデザインを更新する
Author: login: y047aka
Created: 2025-04-09T04:35:38Z | Merged: 2025-04-10T01:14:14Z

- プラチナスポンサーに株式会社スリーシェイクを追加
- heroSectionのデザインを更新
- ゴールドスポンサーのロゴを縮小
- ロゴスポンサーにSiiibo証券株式会社を追加


<img width="1636" alt="スクリーンショット 2025-04-09 13 31 43" src="https://github.com/user-attachments/assets/711f5dbb-97bd-492c-a792-376cecd6307d" />

<img width="1610" alt="スクリーンショット 2025-04-09 13 33 20" src="https://github.com/user-attachments/assets/4dae8000-7efd-4d72-8ffc-d51513f76b39" />



Diff:
diff --git a/app/Route/Index.elm b/app/Route/Index.elm
index 7d0cf3d..3653dfb 100644
--- a/app/Route/Index.elm
+++ b/app/Route/Index.elm
@@ -3,14 +3,14 @@ module Route.Index exposing (ActionData, Data, Model, Msg, route)
 import BackendTask exposing (BackendTask)
 import Css exposing (..)
 import Css.Extra exposing (columnGap, fr, grid, gridColumn, gridTemplateColumns, rowGap)
-import Css.Global exposing (descendants)
+import Css.Global exposing (descendants, withClass)
 import Css.Media as Media exposing (only, screen, withMedia)
 import Effect exposing (Effect)
 import FatalError exposing (FatalError)
 import Head
 import Head.Seo
 import Html.Styled as Html exposing (Html, a, div, h1, h2, h3, iframe, img, li, p, section, span, tbody, td, text, th, thead, tr, ul)
-import Html.Styled.Attributes as Attributes exposing (alt, attribute, class, css, href, rel, src, style)
+import Html.Styled.Attributes as Attributes exposing (alt, attribute, class, css, href, rel, src)
 import PagesMsg exposing (PagesMsg)
 import Random
 import RouteBuilder exposing (App, StatefulRoute)
@@ -102,7 +102,7 @@ view :
 view _ _ model =
     { title = ""
     , body =
-        [ hero
+        [ hero model.seed
         , newsSection
         , aboutSection
         , overviewSection
@@ -112,51 +112,164 @@ view _ _ model =
     }
 
 
-hero : Html msg
-hero =
+hero : Int -> Html msg
+hero seed =
+    div [ css [ padding3 zero (px 10) (px 10) ] ]
+        [ div
+            [ css
+                [ padding3 (px 80) (px 20) (px 20)
+                , display grid
+                , property "justify-items" "center"
+                , rowGap (rem 2.5)
+                , borderRadius (px 10)
+                , property "background-color" "var(--color-grey095)"
+                , property "color" "var(--color-primary)"
+                ]
+            ]
+            [ logoAndDate
+            , heroSponsorsBlock (platinumSponsorsShuffled seed)
+            , socialLinkList
+                [ { id = "x"
+                  , icon = "images/x.svg"
+                  , href = "https://x.com/fp_matsuri"
+                  }
+                , { id = "hatena_blog"
+                  , icon = "images/hatenablog.svg"
+                  , href = "https://blog.fp-matsuri.org/"
+                  }
+                , { id = "fortee"
+                  , icon = "images/fortee.svg"
+                  , href = "https://fortee.jp/2025fp-matsuri"
+                  }
+                ]
+            ]
+        ]
+
+
+logoAndDate : Html msg
+logoAndDate =
     let
+        -- TODO：ロゴイメージとロゴタイプ1枚の画像にする
+        logo =
+            [ img [ src "images/logomark.svg", css [ height (pct 100) ] ] []
+            , h1
+                [ css
+                    [ margin zero
+                    , lineHeight (num 1)
+                    , property "font-family" "var(--serif-logo)"
+                    , fontSize (rem 2.2)
+                    , fontWeight inherit
+                    , withMedia [ only screen [ Media.minWidth (px 640) ] ]
+                        [ fontSize (rem 3.25) ]
+                    ]
+                ]
+                [ text "関数型まつり" ]
+            ]
+
         date =
-            div [ class "date" ]
+            div
+                [ css
+                    [ property "font-family" "var(--montserrat-sans)"
+                    , fontSize (rem 1)
+                    , fontWeight (int 300)
+                    , withMedia [ only screen [ Media.minWidth (px 640) ] ]
+                        [ fontSize (rem 1.5) ]
+                    ]
+                ]
                 [ text "2025.6.14"
-                , span [ style "font-size" "70%" ] [ text " sat" ]
+                , span [ css [ fontSize (pct 70) ] ] [ text " sat" ]
                 , text " – 15"
-                , span [ style "font-size" "70%" ] [ text " sun" ]
+                , span [ css [ fontSize (pct 70) ] ] [ text " sun" ]
                 ]
-
-        iconButton item =
-            a [ class "icon-button", href item.href ]
-                [ img [ class item.id, src item.icon ] [] ]
     in
-    div [ class "hero" ]
-        [ div [ class "hero-main" ]
-            [ img [ class "logomark", src "images/logomark.svg" ] []
-            , h1 [] [ text "関数型まつり" ]
-            , date
+    div
+        [ css
+            [ width (pct 100)
+            , property "display" "grid"
+            , property "grid-template-rows" "6rem auto auto"
+            , property "place-items" "center"
+            , rowGap (rem 1.2)
+            , withMedia [ only screen [ Media.minWidth (px 640) ] ]
+                [ property "grid-template-rows" "9rem auto auto" ]
             ]
-        , ul [ class "links" ] (List.map (\link -> li [] [ iconButton link ]) links)
         ]
+        (logo ++ [ date ])
+
+
+heroSponsorsBlock : List { name : String, image : String, href : String } -> Html msg
+heroSponsorsBlock sponsors =
+    let
+        platinumSponsorLogo sponsor =
+            a
+                [ href sponsor.href
+                , Attributes.rel "noopener noreferrer"
+                , Attributes.target "_blank"
+                , css
+                    [ display block
+                    , width (px 200)
+                    , withMedia [ only screen [ Media.minWidth (px 640) ] ]
+                        [ width (px 250) ]
+                    ]
+                ]
+                [ img
+                    [ src ("images/sponsors/" ++ sponsor.image)
+                    , css
+                        [ backgroundColor (rgb 255 255 255)
+                        , borderRadius (px 10)
+                        , width (pct 100)
+                        ]
+                    , alt sponsor.name
+                    ]
+                    []
+                ]
+    in
+    div [] (List.map platinumSponsorLogo sponsors)
 
 
-links : List { id : String, icon : String, href : String }
-links =
-    [ { id = "x"
-      , icon = "images/x.svg"
-      , href = "https://x.com/fp_matsuri"
-      }
-    , { id = "hatena_blog"
-      , icon = "images/hatenablog.svg"
-      , href = "https://blog.fp-matsuri.org/"
-      }
-    , { id = "fortee"
-      , icon = "images/fortee.svg"
-      , href = "https://fortee.jp/2025fp-matsuri"
-      }
-    ]
+socialLinkList : List { id : String, icon : String, href : String } -> Html msg
+socialLinkList links_ =
+    let
+        iconButton item =
+            a
+                [ href item.href
+                , css
+                    [ width (px 44)
+                    , height (px 44)
+                    , displayFlex
+                    , alignItems center
+                    , justifyContent center
+                    , borderRadius (pct 100)
+                    , backgroundColor (rgba 255 255 255 1)
+                    ]
+                ]
+                [ img
+                    [ class item.id
+                    , src item.icon
+                    , css
+                        [ withClass "x" [ width (pct 50), height (pct 50) ]
+                        , withClass "fortee" [ width (pct 50), height (pct 50) ]
+                        , withClass "hatena_blog" [ width (pct 100), height (pct 100) ]
+                        ]
+                    ]
+                    []
+                ]
+    in
+    ul
+        [ css
+            [ width (pct 100)
+            , margin zero
+            , padding zero
+            , displayFlex
+            , justifyContent flexEnd
+            , columnGap (rem 0.75)
+            ]
+        ]
+        (List.map (\link -> li [ css [ listStyle none ] ] [ iconButton link ]) links_)
 
 
 newsSection : Html msg
 newsSection =
-    section "News"
+    section ""
         [ news
             [ { date = "2025-04-06"
               , label = "🎉 注目のプログラムがついに公開！そしてチケット販売開始しました！！"
@@ -482,11 +595,16 @@ type alias Sponsor =
     }
 
 
+platinumSponsorsShuffled : Int -> List Sponsor
+platinumSponsorsShuffled seed =
+    shuffleList seed
+        [ Sponsor "株式会社スリーシェイク" "3-shake.png" "https://3-shake.com/" ]
+
+
 goldSponsorsShuffled : Int -> List Sponsor
 goldSponsorsShuffled seed =
     shuffleList seed
-        [ Sponsor "株式会社kubell（旧Chatwork株式会社）" "kubell.png" "https://www.kubell.com/recruit/engineer/"
-        ]
+        [ Sponsor "株式会社kubell（旧Chatwork株式会社）" "kubell.png" "https://www.kubell.com/recruit/engineer/" ]
 
 
 silverSponsorsShuffled : Int -> List Sponsor
@@ -504,6 +622,7 @@ logoSponsorsShuffled seed =
     shuffleList seed
         [ Sponsor "合同会社Ignission" "ignission.png" "https://ignission.tech/"
         , Sponsor "株式会社ギークニア" "geekneer.png" "https://geekneer.com/"
+        , Sponsor "Siiibo証券株式会社" "siiibo.png" "https://siiibo.com/"
         ]
 
 
@@ -535,6 +654,7 @@ sponsorLogos seed =
         logoGridStyle =
             batch
                 [ display grid
+                , rowGap (px 10)
                 , columnGap (px 10)
                 , paddingTop (px 20)
                 , justifyContent center
@@ -544,14 +664,25 @@ sponsorLogos seed =
                 ]
     in
     div [ css [ width (pct 100), maxWidth (em 43) ] ]
-        [ sponsorPlanHeader "ゴールドスポンサー"
+        [ sponsorPlanHeader "プラチナスポンサー"
         , div
             [ css
                 [ logoGridStyle
                 , paddingBottom (px 40)
                 , gridTemplateColumns [ fr 1 ]
                 , withMedia [ only screen [ Media.minWidth (px 640) ] ]
-                    [ gridTemplateColumns [ px 280 ] ]
+                    [ gridTemplateColumns [ px 326 ] ]
+                ]
+            ]
+            (List.map sponsorLogo (platinumSponsorsShuffled seed))
+        , sponsorPlanHeader "ゴールドスポンサー"
+        , div
+            [ css
+                [ logoGridStyle
+                , paddingBottom (px 40)
+                , gridTemplateColumns [ fr 1, fr 1 ]
+                , withMedia [ only screen [ Media.minWidth (px 640) ] ]
+                    [ gridTemplateColumns [ px 257 ] ]
                 ]
             ]
             (List.map sponsorLogo (goldSponsorsShuffled seed))
@@ -573,7 +704,7 @@ sponsorLogos seed =
                 , paddingBottom (px 40)
                 , gridTemplateColumns [ fr 1, fr 1, fr 1, fr 1 ]
                 , withMedia [ only screen [ Media.minWidth (px 640) ] ]
-                    [ gridTemplateColumns [ px 116, px 116 ] ]
+                    [ property "grid-template-columns" "repeat(auto-fit, 116px)" ]
                 ]
             ]
             (List.map sponsorLogo (logoSponsorsShuffled seed))
@@ -699,6 +830,10 @@ section : String -> List (Html msg) -> Html msg
 section title children =
     let
         heading =
-            h2 [] [ text title ]
+            if title == "" then
+                text ""
+
+            else
+                h2 [] [ text title ]
     in
     Html.section [] (heading :: children)
diff --git a/public/images/sponsors/3-shake.png b/public/images/sponsors/3-shake.png
new file mode 100644
index 0000000..f2a6694
Binary files /dev/null and b/public/images/sponsors/3-shake.png differ
diff --git a/public/images/sponsors/siiibo.png b/public/images/sponsors/siiibo.png
new file mode 100644
index 0000000..4311e02
Binary files /dev/null and b/public/images/sponsors/siiibo.png differ
diff --git a/style.css b/style.css
index 76a73fc..cffbffa 100644
--- a/style.css
+++ b/style.css
@@ -55,8 +55,6 @@ a {
   grid-template-columns: 150px 1fr;
   align-items: center;
   column-gap: 2rem;
-  box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px,
-    rgba(0, 0, 0, 0.1) 0px 1px 2px -1px;
 }
 
 .site-header > .site-logo {
@@ -154,121 +152,6 @@ section > h2 {
   font-weight: 300;
 }
 
-.hero {
-  padding: 80px 40px 40px;
-  display: grid;
-  justify-items: center;
-  row-gap: 3rem;
-  background-color: var(--color-primary);
-  color: var(--color-on-primary);
-}
-
-.hero > .hero-main {
-  width: 100%;
-  display: grid;
-  grid-template-rows: 6rem auto auto;
-  place-items: center;
-  row-gap: 1.2rem;
-}
-
-@media all and (min-width: 640px) {
-  .hero > .hero-main {
-    grid-template-rows: 9rem auto auto;
-  }
-}
-
-.hero-main > .logomark {
-  height: 100%;
-}
-
-.hero-main > h1 {
-  margin: 0;
-  line-height: 1;
-  font-family: var(--serif-logo);
-  font-size: 2.2rem;
-  font-weight: inherit;
-}
-
-@media all and (min-width: 640px) {
-  .hero-main > h1 {
-    font-size: 3.25rem;
-  }
-}
-
-.hero-main > .date {
-  font-family: var(--montserrat-sans);
-  font-size: 1rem;
-  font-weight: 300;
-}
-
-@media all and (min-width: 640px) {
-  .hero-main > .date {
-    font-size: 1.5rem;
-  }
-}
-
-.hero .announcement {
-  display: flex;
-  flex-direction: column;
-  row-gap: 1rem;
-  padding: 1rem;
-  font-size: 0.875rem;
-  border-radius: 0.375rem;
-  background-color: rgba(255 255 255 / 0.1);
-}
-
-.hero .announcement .buttons {
-  display: flex;
-  flex-direction: column;
-  row-gap: 0.5rem;
-}
-
-.hero .announcement a.button {
-  display: block;
-  padding: 4px;
-  text-align: center;
-  text-decoration: none;
-  white-space: nowrap;
-  border-radius: 6px;
-  background: rgb(233 30 99);
-  color: inherit;
-}
-
-.hero .links {
-  width: 100%;
-  margin: 0;
-  padding: 0;
-
-  display: flex;
-  justify-content: flex-end;
-  column-gap: 1rem;
-}
-
-.hero .links > li {
-  list-style: none;
-}
-
-.hero .icon-button {
-  width: 56px;
-  height: 56px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: 100%;
-  background-color: rgb(222 227 237 / 0.4);
-}
-
-.hero .icon-button > :where(.x, .fortee) {
-  width: 50%;
-  height: 50%;
-}
-
-.hero .icon-button > .hatena_blog {
-  width: 100%;
-  height: 100%;
-}
-
-
 .sponsors {
   max-width: 40em;
   padding-inline: 1.2em;


Reviews:
user: login: y047aka
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T04:40:02Z
user: login: y047aka
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T04:42:08Z
user: login: wm3
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T05:20:31Z
user: login: y047aka
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T05:47:32Z
user: login: y047aka
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T06:15:45Z
user: login: wm3
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T09:53:17Z
user: login: y047aka
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-09T10:09:28Z

Review comments:
diff_hunk: @@ -699,6 +817,10 @@ section : String -> List (Html msg) -> Html msg
 section title children =
     let
         heading =
-            h2 [] [ text title ]
+            if title == "" then
+                text ""
+
+            else
+                h2 [] [ text title ]
path: app/Route/Index.elm
user: login: y047aka
body: Newsセクションの見出しをなくしたので、見出しがない場合を想定するよう変更しました。
Elm独特の書き方ですが、viewの関数で `text ""` は要素やテキストを追加しないことを意味します。
JSXのnullに相当？（JSX普段あまり使わないのでわからない）
created_at: 2025-04-09T04:40:02Z
author_association: COLLABORATOR
diff_hunk: @@ -112,28 +112,128 @@ view _ _ model =
     }
 
 
-hero : Html msg
-hero =
+hero : Int -> Html msg
+hero seed =
path: app/Route/Index.elm
user: login: y047aka
body: 関数内の変数定義など多くなってしまったので、レビュー待つ間にもう少し調整するかもです。
created_at: 2025-04-09T04:42:08Z
author_association: COLLABORATOR
diff_hunk: @@ -699,6 +817,10 @@ section : String -> List (Html msg) -> Html msg
 section title children =
     let
         heading =
-            h2 [] [ text title ]
+            if title == "" then
+                text ""
+
+            else
+                h2 [] [ text title ]
path: app/Route/Index.elm
user: login: wm3
body: > Elm独特の書き方ですが、viewの関数で `text ""` は要素やテキストを追加しないことを意味します。
> JSXのnullに相当？（JSX普段あまり使わないのでわからない）

JSXでテキストを `text ""` のように書かないと思うので該当するものはない気もしますが、今回のケースだと `<h2>{title}</h2>` と書けば同じ事になるんじゃないかという気はしますね。
created_at: 2025-04-09T05:20:31Z
author_association: COLLABORATOR
diff_hunk: @@ -699,6 +817,10 @@ section : String -> List (Html msg) -> Html msg
 section title children =
     let
         heading =
-            h2 [] [ text title ]
+            if title == "" then
+                text ""
+
+            else
+                h2 [] [ text title ]
path: app/Route/Index.elm
user: login: y047aka
body: > 今回のケースだと `<h2>{title}</h2>` と書けば同じ事になるんじゃないかという気はしますね。

これだと h2タグがブラウザにレンダリングされちゃうんです。
この場合にもCSSで `h2:empty { display: none; }` みたいにしてやれば同じ見た目にはなるのですが、そもそもHTMLに現れる必要がないので、h2タグを返さない書き方をしたく。
created_at: 2025-04-09T05:47:32Z
author_association: COLLABORATOR
diff_hunk: @@ -112,28 +112,128 @@ view _ _ model =
     }
 
 
-hero : Html msg
-hero =
+hero : Int -> Html msg
+hero seed =
path: app/Route/Index.elm
user: login: y047aka
body: 簡単に部分ごとの3つの関数に分けました。

- `logoAndDate`
- `heroSponsorsBlock`
- `socialLinkList`
created_at: 2025-04-09T06:15:45Z
author_association: COLLABORATOR
diff_hunk: @@ -699,6 +817,10 @@ section : String -> List (Html msg) -> Html msg
 section title children =
     let
         heading =
-            h2 [] [ text title ]
+            if title == "" then
+                text ""
+
+            else
+                h2 [] [ text title ]
path: app/Route/Index.elm
user: login: wm3
body: あーなるほど。

Reactだと条件付きのレンダリングみたいなパターンが使われる事が多いんじゃないかな…と思いました。 null とか undefined を渡すと要素が表示されない…みたいな。

https://ja.react.dev/learn/conditional-rendering
created_at: 2025-04-09T09:53:17Z
author_association: COLLABORATOR
diff_hunk: @@ -699,6 +817,10 @@ section : String -> List (Html msg) -> Html msg
 section title children =
     let
         heading =
-            h2 [] [ text title ]
+            if title == "" then
+                text ""
+
+            else
+                h2 [] [ text title ]
path: app/Route/Index.elm
user: login: y047aka
body: あ、それですそれそれ
created_at: 2025-04-09T10:09:28Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-09T04:36:35Z
body: Preview deployment available at https://2025fp-matsuri-nhllytwap-keitans-projects.vercel.app
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-09T05:57:41Z
body: Preview deployment available at https://2025fp-matsuri-pt52u6d5a-keitans-projects.vercel.app
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-09T06:03:40Z
body: Preview deployment available at https://2025fp-matsuri-2z9lo584m-keitans-projects.vercel.app
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-09T06:09:07Z
body: Preview deployment available at https://2025fp-matsuri-h1sj7iinw-keitans-projects.vercel.app
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-09T06:14:35Z
body: Preview deployment available at https://2025fp-matsuri-9vvczcku8-keitans-projects.vercel.app
author_association: NONE
user: login: y047aka
created_at: 2025-04-10T01:13:59Z
body: こちらマージさせていただきます。
author_association: COLLABORATOR