diff options
| -rw-r--r-- | assets/cgit.css | 127 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/ui-shared.c | 150 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rwxr-xr-x | tests/t0010-validate-html.sh | 11 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rwxr-xr-x | tests/t0102-summary.sh | 4 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
4 files changed, 189 insertions, 103 deletions
diff --git a/assets/cgit.css b/assets/cgit.css index d10a24d..e02d551 100644 --- a/assets/cgit.css +++ b/assets/cgit.css @@ -21,99 +21,142 @@ div#cgit table { border-collapse: collapse; } -div#cgit table#header { +div#cgit #header { + display: grid; + grid-template-columns: auto 1fr auto; + grid-template-areas: + "logo heading switch" + "logo desc owner"; + align-items: center; + column-gap: 10px; width: 100%; margin-bottom: 1em; } -div#cgit table#header td.logo { - width: 96px; - vertical-align: top; +div#cgit #header .logo { + grid-area: logo; + align-self: start; +} + +div#cgit #header .logo img { + display: block; + max-width: 96px; } -div#cgit table#header td.main { +div#cgit #header h1.main { + grid-area: heading; + margin: 0; font-size: 250%; - padding-left: 10px; + font-weight: normal; white-space: nowrap; } -div#cgit table#header td.main a { +div#cgit #header h1.main a { color: #000; } -div#cgit table#header td.form { - text-align: right; - vertical-align: bottom; - padding-right: 1em; - padding-bottom: 2px; - white-space: nowrap; -} - -div#cgit table#header td.form form, -div#cgit table#header td.form input, -div#cgit table#header td.form select { +div#cgit #header .branch-switch { + grid-area: switch; + justify-self: end; + align-self: end; font-size: 90%; } -div#cgit table#header td.sub { +div#cgit #header .sub { + grid-area: desc; + margin: 0; color: #777; border-top: solid 1px #ccc; - padding-left: 10px; } -div#cgit table.tabs { +div#cgit #header .owner { + grid-area: owner; + justify-self: end; + text-align: right; +} + +@media (max-width: 640px) { + div#cgit #header { + grid-template-columns: auto 1fr; + grid-template-areas: + "logo heading" + "desc desc" + "owner owner" + "switch switch"; + row-gap: 4px; + } + div#cgit #header .branch-switch, + div#cgit #header .owner { + justify-self: start; + text-align: left; + } + div#cgit #header h1.main { + white-space: normal; + } +} + +div#cgit .tabs { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + column-gap: 0.5em; border-bottom: solid 3px #ccc; - border-collapse: collapse; margin-top: 2em; - margin-bottom: 0px; - width: 100%; } -div#cgit table.tabs td { - padding: 0px 1em; - vertical-align: bottom; +div#cgit .tabs ul { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + flex: 1 1 auto; + list-style: none; + margin: 0; + padding: 0; } -div#cgit table.tabs td a { +div#cgit .tabs li { + margin: 0; + padding: 0 1em; +} + +div#cgit .tabs a { + display: inline-block; padding: 2px 0.75em; color: #777; font-size: 110%; } -div#cgit table.tabs td a.active { +div#cgit .tabs a.active { color: #000; background-color: #ccc; } -div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after { +div#cgit .tabs a.external:after { content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhcJDQY+gm2TAAAAHWlUWHRDb21tZW50AAAAAABDcmVhdGVkIHdpdGggR0lNUGQuZQcAAABbSURBVAhbY2BABs4MU4CwhYHBh2Erww4wrGFQZHjI8B8IgUIscJWyDHcggltQhI4zGDCcRwhChPggHIggP1QoAVmQkSETrGoHsiAEsACtBYN0oDAMbgU6EBcAAL2eHUt4XUU4AAAAAElFTkSuQmCC); opacity: 0.5; margin: 0 0 0 5px; } -div#cgit table.tabs td.form { - text-align: right; -} - -div#cgit table.tabs td.form form { +div#cgit .tabs .search { padding-bottom: 2px; font-size: 90%; white-space: nowrap; } -div#cgit table.tabs td.form input, -div#cgit table.tabs td.form select { +div#cgit .tabs .search input, +div#cgit .tabs .search select { font-size: 90%; } -div#cgit div.path { +div#cgit .path { margin: 0px; padding: 5px 2em 2px 2em; color: #000; background-color: #eee; + overflow-wrap: anywhere; } -div#cgit div.content { +div#cgit .content { margin: 0px; padding: 2em; border-bottom: solid 3px #ccc; @@ -662,19 +705,19 @@ div#cgit span.deletions { color: #800; } -div#cgit div.footer { +div#cgit .footer { margin-top: 0.5em; text-align: center; font-size: 80%; color: #ccc; } -div#cgit div.footer a { +div#cgit .footer a { color: #ccc; text-decoration: none; } -div#cgit div.footer a:hover { +div#cgit .footer a:hover { text-decoration: underline; } diff --git a/source/ui-shared.c b/source/ui-shared.c index eb68ab7..69d2cdf 100644 --- a/source/ui-shared.c +++ b/source/ui-shared.c @@ -867,7 +867,7 @@ void cgit_print_docstart(void) void cgit_print_docend(void) { - html("</div> <!-- class=content -->\n"); + html("</main>\n"); if (ctx.cfg.embedded) { html("</div> <!-- id=cgit -->\n"); if (ctx.cfg.footer) @@ -877,10 +877,10 @@ void cgit_print_docend(void) if (ctx.cfg.footer) html_include(ctx.cfg.footer); else { - htmlf("<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> " + htmlf("<footer class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> " "(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string); html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601))); - html("</div>\n"); + html("</footer>\n"); } html("</div> <!-- id=cgit -->\n"); html("</body>\n</html>\n"); @@ -1015,9 +1015,6 @@ static void print_header(void) { char *logo = NULL, *logo_link = NULL; - html("<table id='header'>\n"); - html("<tr>\n"); - if (ctx.repo && ctx.repo->logo && *ctx.repo->logo) logo = ctx.repo->logo; else @@ -1026,44 +1023,47 @@ static void print_header(void) logo_link = ctx.repo->logo_link; else logo_link = ctx.cfg.logo_link; + + html("<header id='header'>\n"); if (logo && *logo) { - html("<td class='logo' rowspan='2'><a href='"); + html("<a class='logo' href='"); if (logo_link && *logo_link) html_attr(logo_link); else html_attr(cgit_rooturl()); html("'><img src='"); html_attr(logo); - html("' alt='cgit logo'/></a></td>\n"); + html("' alt='cgit logo'/></a>\n"); } - html("<td class='main'>"); + html("<h1 class='main'>"); if (ctx.repo) { cgit_index_link("index", NULL, NULL, NULL, NULL, 0, 1); html(" : "); cgit_summary_link(ctx.repo->name, NULL, NULL, NULL); - if (ctx.env.authenticated) { - html("</td><td class='form'>"); - html("<form method='get'>\n"); - cgit_add_hidden_formfields(0, 1, ctx.qry.page); - html("<select name='h' onchange='this.form.submit();'>\n"); - refs_for_each_branch_ref(get_main_ref_store(the_repository), - print_branch_option, ctx.qry.head); - if (ctx.repo->enable_remote_branches) - refs_for_each_remote_ref(get_main_ref_store(the_repository), - print_branch_option, ctx.qry.head); - html("</select> "); - html("<input type='submit' value='switch'/>"); - html("</form>"); - } } else html_txt(ctx.cfg.root_title); - html("</td></tr>\n"); + html("</h1>\n"); + + if (ctx.repo && ctx.env.authenticated) { + html("<form class='branch-switch' method='get'>\n"); + cgit_add_hidden_formfields(0, 1, ctx.qry.page); + html("<select name='h' aria-label='Branch' onchange='this.form.submit();'>\n"); + refs_for_each_branch_ref(get_main_ref_store(the_repository), + print_branch_option, ctx.qry.head); + if (ctx.repo->enable_remote_branches) + refs_for_each_remote_ref(get_main_ref_store(the_repository), + print_branch_option, ctx.qry.head); + html("</select> "); + html("<input type='submit' value='switch'/>"); + html("</form>\n"); + } - html("<tr><td class='sub'>"); if (ctx.repo) { + html("<p class='sub desc'>"); html_txt(ctx.repo->desc); - html("</td><td class='sub right'>"); + html("</p>\n"); + html("<p class='sub owner'>"); if (ctx.repo->owner_filter) { cgit_open_filter(ctx.repo->owner_filter); html_txt(ctx.repo->owner); @@ -1071,11 +1071,13 @@ static void print_header(void) } else { html_txt(ctx.repo->owner); } - } else { - if (ctx.cfg.root_desc) - html_txt(ctx.cfg.root_desc); + html("</p>\n"); + } else if (ctx.cfg.root_desc) { + html("<p class='sub desc'>"); + html_txt(ctx.cfg.root_desc); + html("</p>\n"); } - html("</td></tr></table>\n"); + html("</header>\n"); } void cgit_print_pageheader(void) @@ -1084,39 +1086,63 @@ void cgit_print_pageheader(void) if (!ctx.env.authenticated || !ctx.cfg.noheader) print_header(); - html("<table class='tabs'><tr><td>\n"); + if (ctx.env.authenticated) + html("<nav class='tabs'>\n"); if (ctx.env.authenticated && ctx.repo) { - if (ctx.repo->readme.nr) - reporevlink("about", "about", NULL, + const char *vp = ctx.qry.vpath; + html("<ul>\n"); + if (ctx.repo->readme.nr) { + html("<li>"); + reporevlink("about", "about", "About this repository", hc("about"), ctx.qry.head, NULL, NULL); - cgit_summary_link("summary", NULL, hc("summary"), + html("</li>\n"); + } + html("<li>"); + cgit_summary_link("summary", "Repository summary", hc("summary"), ctx.qry.head); - cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head, + html("</li>\n<li>"); + cgit_refs_link("refs", "Branches and tags", hc("refs"), ctx.qry.head, ctx.qry.oid, NULL); - cgit_log_link("log", NULL, hc("log"), ctx.qry.head, + html("</li>\n<li>"); + cgit_log_link("log", + vp && *vp ? fmt("Commit history of %s", vp) : "Commit history", + hc("log"), ctx.qry.head, NULL, ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, ctx.qry.follow); + html("</li>\n<li>"); if (ctx.qry.page && !strcmp(ctx.qry.page, "blame")) - cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head, + cgit_blame_link("blame", + vp && *vp ? fmt("Line-by-line blame of %s", vp) : "Blame", + hc("blame"), ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); else - cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head, + cgit_tree_link("tree", + vp && *vp ? fmt("Browse the tree at %s", vp) : "Browse the file tree", + hc("tree"), ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); - cgit_commit_link("commit", NULL, hc("commit"), + html("</li>\n<li>"); + cgit_commit_link("commit", "The current commit", hc("commit"), ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); - cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head, + html("</li>\n<li>"); + cgit_diff_link("diff", + vp && *vp ? fmt("Changes to %s", vp) : "Changes in the current commit", + hc("diff"), ctx.qry.head, ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath); - if (ctx.repo->max_stats) - cgit_stats_link("stats", NULL, hc("stats"), + html("</li>\n"); + if (ctx.repo->max_stats) { + html("<li>"); + cgit_stats_link("stats", "Commit activity", hc("stats"), ctx.qry.head, ctx.qry.vpath); + html("</li>\n"); + } if (ctx.repo->homepage) { - html("<a href='"); + html("<li><a class='external' href='"); html_attr(ctx.repo->homepage); - html("'>homepage</a>"); + html("'>homepage</a></li>\n"); } - html("</td><td class='form'>"); - html("<form class='right' method='get' action='"); + html("</ul>\n"); + html("<form class='search' method='get' action='"); if (ctx.cfg.virtual_root) { char *fileurl = cgit_fileurl(ctx.qry.repo, "log", ctx.qry.vpath, NULL); @@ -1125,37 +1151,43 @@ void cgit_print_pageheader(void) } html("'>\n"); cgit_add_hidden_formfields(1, 0, "log"); - html("<select name='qt'>\n"); + html("<select name='qt' aria-label='Search type'>\n"); html_option("grep", "log msg", ctx.qry.grep); html_option("author", "author", ctx.qry.grep); html_option("committer", "committer", ctx.qry.grep); html_option("range", "range", ctx.qry.grep); html("</select>\n"); - html("<input class='txt' type='search' size='10' name='q' value='"); + html("<input class='txt' type='search' name='q' aria-label='Search' value='"); html_attr(ctx.qry.search); html("'/>\n"); html("<input type='submit' value='search'/>\n"); html("</form>\n"); } else if (ctx.env.authenticated) { char *currenturl = cgit_currenturl(); - site_link(NULL, "index", NULL, hc("repolist"), NULL, NULL, 0, 1); - if (ctx.cfg.root_readme) - site_link("about", "about", NULL, hc("about"), + html("<ul>\n<li>"); + site_link(NULL, "index", "Repository index", hc("repolist"), NULL, NULL, 0, 1); + html("</li>\n"); + if (ctx.cfg.root_readme) { + html("<li>"); + site_link("about", "about", "About this site", hc("about"), NULL, NULL, 0, 1); - html("</td><td class='form'>"); - html("<form method='get' action='"); + html("</li>\n"); + } + html("</ul>\n"); + html("<form class='search' method='get' action='"); html_attr(currenturl); html("'>\n"); - html("<input type='search' name='q' size='10' value='"); + html("<input class='txt' type='search' name='q' aria-label='Search' value='"); html_attr(ctx.qry.search); html("'/>\n"); html("<input type='submit' value='search'/>\n"); - html("</form>"); + html("</form>\n"); free(currenturl); } - html("</td></tr></table>\n"); + if (ctx.env.authenticated) + html("</nav>\n"); if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) { - html("<div class='path'>"); + html("<nav class='path' aria-label='breadcrumb'>"); html("path: "); cgit_print_path_crumbs(ctx.qry.vpath); if (ctx.repo->enable_follow_links && !strcmp(ctx.qry.page, "log")) { @@ -1166,9 +1198,9 @@ void cgit_print_pageheader(void) ctx.qry.follow = !ctx.qry.follow; html(")"); } - html("</div>"); + html("</nav>"); } - html("<div class='content'>"); + html("<main class='content'>\n"); } void cgit_print_filemode(unsigned short mode) diff --git a/tests/t0010-validate-html.sh b/tests/t0010-validate-html.sh index ca08d69..91433f8 100755 --- a/tests/t0010-validate-html.sh +++ b/tests/t0010-validate-html.sh @@ -29,6 +29,17 @@ test -n "$tidy" || { exit } +# Releases of tidy that predate HTML5 reject the semantic elements (header, +# nav, main, footer) that cgit emits, reporting them as errors. Skip the +# validation unless tidy is new enough to understand them. +printf '<!DOCTYPE html>\n<html lang="en"><head><title>t</title></head><body><header>x</header></body></html>\n' \ + | "$tidy" -q -e >/dev/null 2>&1 +test $? -ge 2 && { + skip_all='Skipping html validation tests: tidy is too old for HTML5' + test_done + exit +} + test_expect_success 'index page' 'test_url ""' test_expect_success 'foo' 'test_url "foo"' test_expect_success 'foo/log' 'test_url "foo/log"' diff --git a/tests/t0102-summary.sh b/tests/t0102-summary.sh index b8864cb..f1c1e78 100755 --- a/tests/t0102-summary.sh +++ b/tests/t0102-summary.sh @@ -7,7 +7,7 @@ test_expect_success 'generate foo summary' 'cgit_url "foo" >tmp' test_expect_success 'find commit 1' 'grep "commit 1" tmp' test_expect_success 'find commit 5' 'grep "commit 5" tmp' test_expect_success 'find branch master' 'grep "master" tmp' -test_expect_success 'no tags' '! grep "tags" tmp' +test_expect_success 'no tags' '! grep "Tag</th>" tmp' test_expect_success 'clone-url expanded correctly' ' grep "git://example.org/foo.git" tmp ' @@ -17,7 +17,7 @@ test_expect_success 'no commit 45' '! grep "commit 45" tmp' test_expect_success 'find commit 46' 'grep "commit 46" tmp' test_expect_success 'find commit 50' 'grep "commit 50" tmp' test_expect_success 'find branch master' 'grep "master" tmp' -test_expect_success 'no tags' '! grep "tags" tmp' +test_expect_success 'no tags' '! grep "Tag</th>" tmp' test_expect_success 'clone-url expanded correctly' ' grep "git://example.org/bar.git" tmp ' |
