Fix up landing page colors

This commit is contained in:
Ben Visness 2021-04-23 23:27:45 -05:00
parent a04b00c0a7
commit 292c400dfb
11 changed files with 220 additions and 168 deletions

View File

@ -7438,6 +7438,15 @@ article code {
.mw-site {
max-width: 80rem; }
.mh-3 {
max-height: 4rem; }
.mh-4 {
max-height: 8rem; }
.mh-5 {
max-height: 16rem; }
.mh-100 {
max-height: 100%; }
@ -8576,17 +8585,6 @@ input[type=submit] {
border-bottom-color: transparent; }
.thread .title:hover {
border-bottom-color: initial; }
.thread.read {
color: #555;
color: var(--forum-thread-read-color); }
.thread.read td {
color: #555;
color: var(--forum-thread-read-color); }
.thread.read a {
color: #888;
color: var(--forum-thread-read-link-color); }
.thread.read .title {
font-weight: 500; }
.forum .thread .info th {
width: 50px; }
@ -8598,18 +8596,18 @@ input[type=submit] {
overflow: hidden;
background-color: #bbb;
background-color: var(--dimmest-color); }
.thread .avatar-icon:not(.lite) {
left: 30px;
bottom: 10px; }
.thread .info .avatar-icon:not(.lite) {
bottom: 0px;
left: 0px; }
.feed .avatar-icon:not(.lite) {
left: -50px;
bottom: -10px; }
.project .box .avatar-icon:not(.lite) {
left: 0px;
bottom: -10px; }
.read {
color: #555;
color: var(--forum-thread-read-color); }
.read td {
color: #555;
color: var(--forum-thread-read-color); }
.read a {
color: #888;
color: var(--forum-thread-read-link-color); }
.read .title {
font-weight: 500; }
.goto {
font-size: 200%;
@ -9220,6 +9218,10 @@ span.icon-rss::before {
width: 10rem;
height: 10rem; } }
.landing .excerpt-fade {
background-image: linear-gradient(to top, var(--content-background), rgba(0, 0, 0, 0));
pointer-events: none; }
.star-btn {
border-bottom-width: 2px;
background-color: #fff;

View File

@ -72,11 +72,18 @@ func (it *StructQueryIterator) Next() (interface{}, bool) {
field = field.Elem()
}
// Some actual values still come through as pointers (like net.IPNet). Dunno why.
// Regardless, we know it's not nil, so we can get at the contents.
valReflected := reflect.ValueOf(val)
if valReflected.Kind() == reflect.Ptr {
valReflected = valReflected.Elem()
}
switch field.Kind() {
case reflect.Int:
field.SetInt(reflect.ValueOf(val).Int())
field.SetInt(valReflected.Int())
default:
field.Set(reflect.ValueOf(val))
field.Set(valReflected)
}
}

View File

@ -248,6 +248,18 @@ article code {
max-width: 80rem;
}
.mh-3 {
max-height: $height-3;
}
.mh-4 {
max-height: $height-4;
}
.mh-5 {
max-height: $height-5;
}
.mh-100 {
max-height: 100%;
}

View File

@ -47,22 +47,6 @@
}
}
&.read {
@include usevar('color', 'forum-thread-read-color');
td {
@include usevar('color', 'forum-thread-read-color');
}
a {
@include usevar('color', 'forum-thread-read-link-color');
}
.title {
font-weight: 500;
}
}
.forum & .info th {
width: 50px;
}
@ -75,27 +59,21 @@
border-radius: 100%;
overflow: hidden;
@include usevar(background-color, dimmest-color);
}
&:not(.lite) {
.thread & {
left: 30px;
bottom: 10px;
}
.read {
@include usevar('color', 'forum-thread-read-color');
.thread .info & {
bottom: 0px;
left: 0px;
}
td {
@include usevar('color', 'forum-thread-read-color');
}
.feed & {
left: -50px;
bottom: -10px;
}
a {
@include usevar('color', 'forum-thread-read-link-color');
}
.project .box & {
left: 0px;
bottom: -10px;
}
.title {
font-weight: 500;
}
}

View File

@ -83,4 +83,9 @@
height: 10rem;
}
}
.excerpt-fade {
background-image: linear-gradient(to top, var(--content-background) , rgba(0, 0, 0, 0));
pointer-events: none;
}
}

View File

@ -1,10 +1,10 @@
{{/*
This template is intended to display a single post or thread in the context of a forum, the feed, or a similar layout.
It should be called with PostListItemData.
It should be called with PostListItem.
*/}}
<div class="flex items-center ph3 pv2">
<div class="post-list-item flex items-center ph3 pv2 {{ if .Unread }}unread{{ else }}read{{ end }}">
<img class="avatar-icon mr2" src="{{ .User.AvatarUrl }}">
<div class="flex-grow-1 overflow-hidden">
<div class="breadcrumbs">

View File

@ -1,5 +1,33 @@
{{ template "base.html" . }}
{{ define "extrahead" }}
<link rel="stylesheet" type="text/css" href="{{ static "landing.css" }}"/>
<script type="text/javascript" src="{{ static "util.js" }}"></script>
<style type="text/css">
{{ $base := . }}
{{ range $col := .PostColumns }}
{{ range $entry := $col }}
{{ $c1 := hex2color .Project.Color1 }}
{{ $linkColor := eq $base.Theme "dark" | ternary (lightness 0.55 $c1) (lightness 0.35 $c1) | color2css }}
{{ $linkHoverColor := eq $base.Theme "dark" | ternary (lightness 0.65 $c1) (lightness 0.45 $c1) | color2css }}
{{ $projectPostBackground := eq $base.Theme "dark" | ternary (lightness 0.15 $c1) (lightness 0.95 $c1) | alpha 0.2 | color2css }}
#p{{ .Project.Subdomain }} a.project-title { color: {{ $linkColor }}; }
#p{{ .Project.Subdomain }} .unread a { color: {{ $linkColor }}; }
#p{{ .Project.Subdomain }} .unread a:hover { color: {{ $linkHoverColor }} }
#p{{ .Project.Subdomain }} .unread .avatar-icon { border-color: {{ $linkColor }}; }
#p{{ .Project.Subdomain }} .post-list-item:nth-of-type(even) { background-color: {{ $projectPostBackground }}; }
#p{{ .Project.Subdomain }} .thread.more { background-color:transparent; }
{{ end }}
{{ end }}
</style>
{{/*
<script type="text/javascript" src="{% static 'templates.js' %}?v={% cachebust %}"></script>
<script type="text/javascript" src="{% static 'timeline.js' %}?v={% cachebust %}"></script>
<script type="text/javascript" src="{% static 'showcase.js' %}?v={% cachebust %}"></script>
*/}}
{{ end }}
{{ define "content" }}
<div class="content-block">
<div class="optionbar pb2">
@ -33,58 +61,41 @@
</div>
<div class="content-block news cf">
{{ $newsPost := .NewsPost }}
{{ range $i, $col := .PostColumns }}
<div class="fl w-100 w-50-l">
<div class="mw7 mw-none-l center-layout">
{{ if eq $i 0 }}
<div class="pt3">
Wow, a featured post!
{{/* {% include "blog_index_thread_list_entry.html" with post=featured_post align_top=True %} */}}
{{ template "landing_page_featured_post" $newsPost}}
</div>
{{ end }}
{{ range $entry := $col }}
{{ $proj := $entry.Project }}
{{ $posts := $entry.Posts }}
<div class="pt3" id="p{{ $proj.Subdomain }}"> {{/* TODO: Is this ID used for anything? */}}
<a {{/* TODO: Replace this special-case style with a CSS class */}}
<div class="pt3" id="p{{ $proj.Subdomain }}">
{{ $c1 := hex2color $proj.Color1 }}
<a
class="project-title"
href="{{ projecturl "/" $proj }}"
style="color: #{{ eq $.Theme "dark" | ternary (brighten $proj.Color1 0.1) (darken $proj.Color1 0.2) }}"
>
<h2 class="ph3">{{ $proj.Name }}</h2>
</a>
{{ with $entry.FeaturedPost }}
<div class="flex items-start ph3 pv2">
<img class="avatar-icon mr2" src="{{ .User.AvatarUrl }}">
<div class="flex-grow-1">
<div class="overflow-hidden">
<div class="title nowrap truncate"><a href="{{ .Url }}">{{ .Title }}</a></div>
<div class="details">
<a class="user" href="{{ .User.ProfileUrl }}">{{ .User.Name }}</a> &mdash; <span class="datetime">{{ relativedate .Date }}</span>
</div>
</div>
<div>
{{ .Content }}
</div>
</div>
</div>
{{ template "landing_page_featured_post" . }}
{{ end }}
{{ range $post := $posts }}
{{ template "post_list_item.html" $post }}
{{ end }}
{{/*
{% with more=posts|length|add:-5|clamp_lower:0 %}
{% if more > 0 %}
<div class="ph3 thread unread more">
<a class="title"
href="{% url 'project_forum' subdomain=proj.slug %}"
>{{ more }} more recently &rarr;</a>
</div>
{% endif %}
{% endwith %}
*/}}
<div class="ph3 thread unread more">
<a class="title" href="{{ projecturl "/forums" $proj }}">
More posts &rarr;
</a>
</div>
</div>
{{ end }}
</div>
@ -94,43 +105,6 @@
{{ end }}
{{/*
{{ define "extrahead" }}
<link rel="stylesheet" type="text/css" href="{{ static "landing.css" }}"/>
<script type="text/javascript" src="{{ static "util.js" }}"></script>
<style type="text/css">
{{ range _, $col := .RecentPostColumns }}
{{ range _, $entry := $col }}
{{ $themeDim := eq .Theme "dark" | ternary (darken .Color 0.5) (brighten .Color 0.2) }}
{{ $themeDimmer := eq .Theme "dark" | ternary (darken .Color 0.65) (brighten .Color 0.4) }}
{{ $themeDimmest := eq .Theme "dark" | ternary (darken .Color 0.8) (brighten .Color 0.6) }}
{{ $linkColor := eq .Theme "dark" | ternary (brighten .Color 0.1) (darken .Color 0.2) }}
{{ $linkHoverColor := eq .Theme "dark" | ternary (brighten .Color 0.2) (darken .Color 0.1) }}
{{ eq .Theme "dark" }}
#p{{ .Project.Subdomain }} .unread a { color: #{% rgb_accent entry.project.color_1 0.55 %}; }
#p{{ .Project.Subdomain }} .unread a:hover { color: #{% rgb_accent entry.project.color_1 0.65 %}; }
#p{{ .Project.Subdomain }} .unread .avatar-icon { border: 2px solid #{% rgb_accent entry.project.color_1 0.55 %}; }
#p{{ .Project.Subdomain }} .thread:nth-of-type(even) { background-color:#{% rgb_accent entry.project.color_1 0.14 False 0.03%}; }
#p{{ .Project.Subdomain }} .forum .post:nth-of-type(even) { background-color:#{% rgb_accent entry.project.color_1 0.14 False 0.03 %}; }
#p{{ .Project.Subdomain }} .blog .post:nth-of-type(even) { background-color:#{% rgb_accent entry.project.color_1 0.14 False 0.03 %}; }
{{ else }}
#p{{ .Project.Subdomain }} .unread a { color: #{% rgb_accent entry.project.color_1 0.35 %}; }
#p{{ .Project.Subdomain }} .unread a:hover { color: #{% rgb_accent entry.project.color_1 0.45 %}; }
#p{{ .Project.Subdomain }} .unread .avatar-icon { border: 2px solid #{% rgb_accent entry.project.color_1 0.35 %}; }
#p{{ .Project.Subdomain }} .thread:nth-of-type(even) { background-color:#{% rgb_accent entry.project.color_1 0.94 False 0.2 %}; }
#p{{ .Project.Subdomain }} .forum .post:nth-of-type(even) { background-color:#{% rgb_accent entry.project.color_1 0.94 False 0.2 %}; }
#p{{ .Project.Subdomain }} .blog .post:nth-of-type(even) { background-color:#{% rgb_accent entry.project.color_1 0.94 False 0.2 %}; }
{{ end }}
#p{{ .Project.Subdomain }} .thread.more { background-color:transparent; }
{{ end }}
{{ end }}
</style>
<script type="text/javascript" src="{% static 'templates.js' %}?v={% cachebust %}"></script>
<script type="text/javascript" src="{% static 'timeline.js' %}?v={% cachebust %}"></script>
<script type="text/javascript" src="{% static 'showcase.js' %}?v={% cachebust %}"></script>
{{ end }}
{% block columns %}
{% include "showcase/js_templates.html" %}
{% include "timeline/js_templates.html" %}
@ -288,4 +262,28 @@
</div>
{% endspaceless %}
{% endblock %}
*/}}
*/}}
{{ define "landing_page_featured_post" }}
{{/* Call this template with a LandingPageFeaturedPost. */}}
<div class="flex items-start ph3 pv2 {{ if .Unread }}unread{{ else }}read{{ end }}">
<img class="avatar-icon mr2" src="{{ .User.AvatarUrl }}">
<div class="flex-grow-1">
<div class="overflow-hidden">
<div class="title nowrap truncate"><a href="{{ .Url }}">{{ .Title }}</a></div>
<div class="details">
<a class="user" href="{{ .User.ProfileUrl }}">{{ .User.Name }}</a> &mdash; <span class="datetime">{{ relativedate .Date }}</span>
</div>
</div>
<div class="overflow-hidden mh-5 mt2 relative">
<div>
{{ .Content }}
</div>
<div class="excerpt-fade absolute w-100 h4 bottom-0"></div>
</div>
<div class="mt2">
<a href="{{ .Url }}">Read More &rarr;</a>
</div>
</div>
</div>
{{ end }}

View File

@ -31,8 +31,8 @@
background-size: "{{ . }}";
{{ end }}
{{ else }}
{{ $bgcolor := or .Project.Color1 "999999" }}
background-color: #{{ eq .Theme "dark" | ternary (darken $bgcolor 0.6) (brighten $bgcolor 0.6) }};
{{ $bgcolor := or .Project.Color1 "999999" | hex2color }}
background-color: {{ eq .Theme "dark" | ternary (darken 0.6 $bgcolor) (brighten 0.6 $bgcolor) | color2css }};
background-image: url('data:image/png;base64,{{ eq .Theme "dark" | ternary $bgdark $bglight }}');
background-size: auto;
{{ end }}

View File

@ -1,43 +1,44 @@
{{ $themeDim := eq .Theme "dark" | ternary (darken .Color 0.5) (brighten .Color 0.2) }}
{{ $themeDimmer := eq .Theme "dark" | ternary (darken .Color 0.65) (brighten .Color 0.4) }}
{{ $themeDimmest := eq .Theme "dark" | ternary (darken .Color 0.8) (brighten .Color 0.6) }}
{{ $c := hex2color .Color }}
{{ $themeDim := eq .Theme "dark" | ternary (lightness 0.35 $c) (lightness 0.75 $c) | color2css }}
{{ $themeDimmer := eq .Theme "dark" | ternary (lightness 0.3 $c) (lightness 0.8 $c) | color2css }}
{{ $themeDimmest := eq .Theme "dark" | ternary (lightness 0.2 $c) (lightness 0.85 $c) | color2css }}
{{ $linkColor := eq .Theme "dark" | ternary (brighten .Color 0.1) (darken .Color 0.2) }}
{{ $linkHoverColor := eq .Theme "dark" | ternary (brighten .Color 0.2) (darken .Color 0.1) }}
{{ $linkColor := eq .Theme "dark" | ternary (lightness 0.55 $c) (lightness 0.35 $c) | color2css }}
{{ $linkHoverColor := eq .Theme "dark" | ternary (lightness 0.65 $c) (lightness 0.45 $c) | color2css }}
:root {
--theme-color: #{{ .Color }};
--theme-color-dim: #{{ $themeDim }};
--theme-color-dimmer: #{{ $themeDimmer }};
--theme-color-dimmest: #{{ $themeDimmest }};
--theme-color: {{ $c | color2css }};
--theme-color-dim: {{ $themeDim }};
--theme-color-dimmer: {{ $themeDimmer }};
--theme-color-dimmest: {{ $themeDimmest }};
--link-color: #{{ $linkColor }};
--link-color-hover: #{{ $linkHoverColor }};
--link-color: {{ $linkColor }};
--link-color-hover: {{ $linkHoverColor }};
}
.accent {
background-color: #{{ $themeDim }};
background-color: {{ $themeDim }};
background-color: var(--theme-dim);
}
.user-bar {
border-bottom-color: #{{ $themeDim }};
border-bottom-color: {{ $themeDim }};
border-bottom-color: var(--theme-dim);
}
header .content-title .subtitle {
border-top-color: #{{ $themeDim }};
border-top-color: {{ $themeDim }};
border-top-color: var(--theme-dim);
}
a, .thread:before, button, .button, input[type=button], input[type=submit] {
color: #{{ $linkColor }};
color: {{ $linkColor }};
color: var(--link-color);
}
a:hover, button:hover, .button:hover, input[type=button]:hover, input[type=submit]:hover {
color: #{{ $linkHoverColor }};
color: {{ $linkHoverColor }};
color: var(--link-hover-color);
}
.unread .avatar-icon {
border: 2px solid #{{ $linkColor }};
border: 2px solid {{ $linkColor }};
border: 2px solid var(--link-color);
}
@ -91,7 +92,7 @@ all of this CSS.
{% endif %} */
:root {
--background-even-background: #{{ eq .Theme "dark" | ternary (darken .Color 0.8) (brighten .Color 0.9) }};
--background-even-background: {{ eq .Theme "dark" | ternary (lightness 0.15 $c) (lightness 0.95 $c) | color2css }};
}
/* Assets */

View File

@ -67,15 +67,19 @@ func names(ts []*template.Template) []string {
}
var HMNTemplateFuncs = template.FuncMap{
"brighten": func(hexColor string, amount float64) (string, error) {
if len(hexColor) < 6 {
return "", fmt.Errorf("couldn't brighten invalid hex color: %v", hexColor)
}
return noire.NewHex(hexColor).Tint(amount).Hex(), nil
"alpha": func(alpha float64, color noire.Color) noire.Color {
color.Alpha = alpha
return color
},
"brighten": func(amount float64, color noire.Color) noire.Color {
return color.Tint(amount)
},
"cachebust": func() string {
return cachebust
},
"color2css": func(color noire.Color) template.CSS {
return template.CSS(color.HTML())
},
"currentprojecturl": func(url string) string {
return hmnurl.Url(url, nil) // TODO: Use project subdomain
},
@ -83,11 +87,18 @@ var HMNTemplateFuncs = template.FuncMap{
absUrl := hmnurl.Url(url, nil)
return fmt.Sprintf("%s?%s", absUrl, query) // TODO: Use project subdomain
},
"darken": func(hexColor string, amount float64) (string, error) {
if len(hexColor) < 6 {
return "", fmt.Errorf("couldn't darken invalid hex color: %v", hexColor)
"darken": func(amount float64, color noire.Color) noire.Color {
return color.Shade(amount)
},
"hex2color": func(hex string) (noire.Color, error) {
if len(hex) < 6 {
return noire.Color{}, fmt.Errorf("hex color was invalid: %v", hex)
}
return noire.NewHex(hexColor).Shade(amount).Hex(), nil
return noire.NewHex(hex), nil
},
"lightness": func(lightness float64, color noire.Color) noire.Color {
h, s, _, a := color.HSLA()
return noire.NewHSLA(h, s, lightness*100, a)
},
"projecturl": func(url string, proj interface{}) string {
return hmnurl.ProjectUrl(url, nil, getProjectSubdomain(proj))

View File

@ -1,6 +1,7 @@
package website
import (
"html/template"
"net/http"
"time"
@ -13,6 +14,7 @@ import (
type LandingTemplateData struct {
templates.BaseData
NewsPost LandingPageFeaturedPost
PostColumns [][]LandingPageProject
ShowcaseTimelineJson string
}
@ -29,7 +31,7 @@ type LandingPageFeaturedPost struct {
User templates.User
Date time.Time
Unread bool
Content string
Content template.HTML
}
func Index(c *RequestContext) ResponseData {
@ -67,7 +69,7 @@ func Index(c *RequestContext) ResponseData {
for _, projRow := range allProjects {
proj := projRow.(*models.Project)
type ProjectPost struct {
type projectPostQuery struct {
Post models.Post `db:"post"`
Thread models.Thread `db:"thread"`
Cat models.Category `db:"cat"`
@ -75,8 +77,7 @@ func Index(c *RequestContext) ResponseData {
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
CatLastReadTime *time.Time `db:"clri.lastread"`
}
projectPostIter, err := db.Query(c.Context(), c.Conn, ProjectPost{},
projectPostIter, err := db.Query(c.Context(), c.Conn, projectPostQuery{},
`
SELECT $columns
FROM
@ -116,7 +117,7 @@ func Index(c *RequestContext) ResponseData {
}
for _, projectPostRow := range projectPosts {
projectPost := projectPostRow.(*ProjectPost)
projectPost := projectPostRow.(*projectPostQuery)
hasRead := false
if projectPost.ThreadLastReadTime != nil && projectPost.ThreadLastReadTime.After(projectPost.Post.PostDate) {
@ -148,20 +149,18 @@ func Index(c *RequestContext) ResponseData {
}
content := contentResult.(*featuredContentResult).Content
// c.Logger.Debug().Str("content", content).Msg("")
landingPageProject.FeaturedPost = &LandingPageFeaturedPost{
Title: projectPost.Thread.Title,
Url: templates.PostUrl(projectPost.Post, projectPost.Cat.Kind, proj.Subdomain()), // TODO
Url: templates.PostUrl(projectPost.Post, projectPost.Cat.Kind, proj.Subdomain()),
User: templates.UserToTemplate(&projectPost.User),
Date: projectPost.Post.PostDate,
Unread: !hasRead,
Content: content,
Content: template.HTML(content),
}
} else {
landingPageProject.Posts = append(landingPageProject.Posts, templates.PostListItem{
Title: projectPost.Thread.Title,
Url: templates.PostUrl(projectPost.Post, projectPost.Cat.Kind, proj.Subdomain()), // TODO
Url: templates.PostUrl(projectPost.Post, projectPost.Cat.Kind, proj.Subdomain()),
User: templates.UserToTemplate(&projectPost.User),
Date: projectPost.Post.PostDate,
Unread: !hasRead,
@ -230,12 +229,51 @@ func Index(c *RequestContext) ResponseData {
}
}
type newsPostQuery struct {
Post models.Post `db:"post"`
PostVersion models.PostVersion `db:"ver"`
Thread models.Thread `db:"thread"`
User models.User `db:"auth_user"`
}
newsPostRow, err := db.QueryOne(c.Context(), c.Conn, newsPostQuery{},
`
SELECT $columns
FROM
handmade_post AS post
JOIN handmade_thread AS thread ON post.thread_id = thread.id
JOIN handmade_category AS cat ON thread.category_id = cat.id
JOIN auth_user ON post.author_id = auth_user.id
JOIN handmade_postversion AS ver ON post.current_id = ver.id
WHERE
cat.project_id = $1
AND cat.kind = $2
AND post.id = thread.first_id
AND thread.moderated = 0
ORDER BY post.postdate DESC
LIMIT 1
`,
models.HMNProjectID,
models.CatTypeBlog,
)
if err != nil {
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news post"))
}
newsPostResult := newsPostRow.(*newsPostQuery)
baseData := getBaseData(c)
baseData.BodyClasses = append(baseData.BodyClasses, "hmdev", "landing") // TODO: Is "hmdev" necessary any more?
var res ResponseData
err = res.WriteTemplate("index.html", LandingTemplateData{
BaseData: getBaseData(c),
BaseData: baseData,
NewsPost: LandingPageFeaturedPost{
Title: newsPostResult.Thread.Title,
Url: templates.PostUrl(newsPostResult.Post, models.CatTypeBlog, ""),
User: templates.UserToTemplate(&newsPostResult.User),
Date: newsPostResult.Post.PostDate,
Unread: true, // TODO
Content: template.HTML(newsPostResult.PostVersion.TextParsed),
},
PostColumns: cols,
})
if err != nil {