Browse Source

Redesign public profiles and toots (#8068)

pull/4/head
Eugen Rochko 4 years ago
committed by GitHub
parent
commit
bb71538bb5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
93 changed files with 1382 additions and 1417 deletions
  1. +5
    -0
      app/controllers/concerns/account_controller_concern.rb
  2. +5
    -0
      app/controllers/statuses_controller.rb
  3. +46
    -0
      app/helpers/stream_entries_helper.rb
  4. +28
    -22
      app/javascript/mastodon/components/relative_timestamp.js
  5. +32
    -13
      app/javascript/packs/public.js
  6. +1
    -1
      app/javascript/styles/application.scss
  7. +75
    -484
      app/javascript/styles/mastodon/accounts.scss
  8. +2
    -3
      app/javascript/styles/mastodon/basics.scss
  9. +12
    -0
      app/javascript/styles/mastodon/components.scss
  10. +573
    -0
      app/javascript/styles/mastodon/containers.scss
  11. +129
    -28
      app/javascript/styles/mastodon/footer.scss
  12. +0
    -111
      app/javascript/styles/mastodon/landing_strip.scss
  13. +84
    -306
      app/javascript/styles/mastodon/stream_entries.scss
  14. +2
    -0
      app/javascript/styles/mastodon/variables.scss
  15. +123
    -0
      app/javascript/styles/mastodon/widgets.scss
  16. +3
    -2
      app/models/concerns/account_header.rb
  17. +15
    -0
      app/views/accounts/_bio.html.haml
  18. +0
    -28
      app/views/accounts/_follow_button.html.haml
  19. +0
    -8
      app/views/accounts/_follow_grid.html.haml
  20. +0
    -3
      app/views/accounts/_follow_grid_hidden.html.haml
  21. +0
    -12
      app/views/accounts/_grid_card.html.haml
  22. +37
    -45
      app/views/accounts/_header.html.haml
  23. +6
    -5
      app/views/accounts/_moved.html.haml
  24. +0
    -1
      app/views/accounts/_nothing_here.html.haml
  25. +36
    -33
      app/views/accounts/show.html.haml
  26. +16
    -0
      app/views/application/_card.html.haml
  27. +6
    -0
      app/views/application/_sidebar.html.haml
  28. +1
    -1
      app/views/auth/registrations/new.html.haml
  29. +0
    -23
      app/views/authorize_follows/_card.html.haml
  30. +1
    -1
      app/views/authorize_follows/show.html.haml
  31. +1
    -1
      app/views/authorize_follows/success.html.haml
  32. +7
    -2
      app/views/follower_accounts/index.html.haml
  33. +7
    -2
      app/views/following_accounts/index.html.haml
  34. +44
    -11
      app/views/layouts/public.html.haml
  35. +1
    -1
      app/views/remote_follow/new.html.haml
  36. +1
    -1
      app/views/remote_unfollows/success.html.haml
  37. +1
    -1
      app/views/settings/migrations/show.html.haml
  38. +1
    -2
      app/views/settings/profiles/show.html.haml
  39. +0
    -6
      app/views/shared/_landing_strip.html.haml
  40. +0
    -7
      app/views/stream_entries/_content_spoiler.html.haml
  41. +17
    -17
      app/views/stream_entries/_detailed_status.html.haml
  42. +0
    -4
      app/views/stream_entries/_media.html.haml
  43. +0
    -2
      app/views/stream_entries/_more.html.haml
  44. +24
    -10
      app/views/stream_entries/_simple_status.html.haml
  45. +12
    -11
      app/views/stream_entries/_status.html.haml
  46. +1
    -1
      app/views/stream_entries/embed.html.haml
  47. +6
    -5
      app/views/stream_entries/show.html.haml
  48. +0
    -5
      config/locales/ar.yml
  49. +0
    -3
      config/locales/ast.yml
  50. +0
    -5
      config/locales/bg.yml
  51. +0
    -5
      config/locales/ca.yml
  52. +0
    -5
      config/locales/co.yml
  53. +0
    -3
      config/locales/da.yml
  54. +0
    -5
      config/locales/de.yml
  55. +0
    -5
      config/locales/el.yml
  56. +10
    -5
      config/locales/en.yml
  57. +0
    -5
      config/locales/eo.yml
  58. +0
    -5
      config/locales/es.yml
  59. +0
    -5
      config/locales/eu.yml
  60. +0
    -5
      config/locales/fa.yml
  61. +0
    -5
      config/locales/fi.yml
  62. +0
    -5
      config/locales/fr.yml
  63. +0
    -5
      config/locales/gl.yml
  64. +0
    -5
      config/locales/he.yml
  65. +0
    -5
      config/locales/hr.yml
  66. +0
    -5
      config/locales/hu.yml
  67. +0
    -5
      config/locales/id.yml
  68. +0
    -5
      config/locales/io.yml
  69. +0
    -5
      config/locales/it.yml
  70. +0
    -5
      config/locales/ja.yml
  71. +0
    -5
      config/locales/ko.yml
  72. +0
    -5
      config/locales/nl.yml
  73. +0
    -5
      config/locales/no.yml
  74. +0
    -5
      config/locales/oc.yml
  75. +0
    -5
      config/locales/pl.yml
  76. +0
    -5
      config/locales/pt-BR.yml
  77. +0
    -5
      config/locales/pt.yml
  78. +0
    -5
      config/locales/ru.yml
  79. +0
    -5
      config/locales/sk.yml
  80. +0
    -1
      config/locales/sl.yml
  81. +0
    -5
      config/locales/sr-Latn.yml
  82. +0
    -5
      config/locales/sr.yml
  83. +0
    -5
      config/locales/sv.yml
  84. +0
    -5
      config/locales/th.yml
  85. +0
    -5
      config/locales/tr.yml
  86. +0
    -5
      config/locales/uk.yml
  87. +0
    -5
      config/locales/zh-CN.yml
  88. +0
    -5
      config/locales/zh-HK.yml
  89. +0
    -5
      config/locales/zh-TW.yml
  90. +5
    -1
      lib/mastodon/version.rb
  91. +1
    -0
      package.json
  92. +1
    -0
      spec/views/stream_entries/show.html.haml_spec.rb
  93. +4
    -0
      yarn.lock

+ 5
- 0
app/controllers/concerns/account_controller_concern.rb View File

@ -8,6 +8,7 @@ module AccountControllerConcern
included do
layout 'public'
before_action :set_account
before_action :set_instance_presenter
before_action :set_link_headers
before_action :check_account_suspension
end
@ -18,6 +19,10 @@ module AccountControllerConcern
@account = Account.find_local!(params[:account_username])
end
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
def set_link_headers
response.headers['Link'] = LinkHeader.new(
[

+ 5
- 0
app/controllers/statuses_controller.rb View File

@ -12,6 +12,7 @@ class StatusesController < ApplicationController
before_action :set_account
before_action :set_status
before_action :set_instance_presenter
before_action :set_link_headers
before_action :check_account_suspension
before_action :redirect_to_original, only: [:show]
@ -148,6 +149,10 @@ class StatusesController < ApplicationController
raise ActiveRecord::RecordNotFound
end
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
def check_account_suspension
gone if @account.suspended?
end

+ 46
- 0
app/helpers/stream_entries_helper.rb View File

@ -12,6 +12,52 @@ module StreamEntriesHelper
end
end
def account_action_button(account)
if user_signed_in?
if account.id == current_user.account_id
link_to settings_profile_url, class: 'button logo-button' do
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('settings.edit_profile')])
end
elsif current_account.following?(account) || current_account.requested?(account)
link_to account_unfollow_path(account), class: 'button logo-button', data: { method: :post } do
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')])
end
else
link_to account_follow_path(account), class: 'button logo-button', data: { method: :post } do
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
end
end
else
link_to account_remote_follow_path(account), class: 'button logo-button modal-button', target: '_new' do
safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.follow')])
end
end
end
def account_badge(account)
if account.bot?
content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')
elsif Setting.show_staff_badge && account.user_staff?
content_tag(:div, class: 'roles') do
if account.user_admin?
content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin')
elsif account.user_moderator?
content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator')
end
end
end
end
def link_to_more(url)
link_to t('statuses.show_more'), url, class: 'load-more load-gap'
end
def nothing_here(extra_classes = '')
content_tag(:div, class: "nothing-here #{extra_classes}") do
t('accounts.nothing_here')
end
end
def account_description(account)
prepend_str = [
[

+ 28
- 22
app/javascript/mastodon/components/relative_timestamp.js View File

@ -60,6 +60,32 @@ const getUnitDelay = units => {
}
};
export const timeAgoString = (intl, date, now, year) => {
const delta = now - date.getTime();
let relativeTime;
if (delta < 10 * SECOND) {
relativeTime = intl.formatMessage(messages.just_now);
} else if (delta < 7 * DAY) {
if (delta < MINUTE) {
relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) });
} else if (delta < HOUR) {
relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) });
} else if (delta < DAY) {
relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) });
} else {
relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) });
}
} else if (date.getFullYear() === year) {
relativeTime = intl.formatDate(date, shortDateFormatOptions);
} else {
relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' });
}
return relativeTime;
};
@injectIntl
export default class RelativeTimestamp extends React.Component {
@ -121,28 +147,8 @@ export default class RelativeTimestamp extends React.Component {
render () {
const { timestamp, intl, year } = this.props;
const date = new Date(timestamp);
const delta = this.state.now - date.getTime();
let relativeTime;
if (delta < 10 * SECOND) {
relativeTime = intl.formatMessage(messages.just_now);
} else if (delta < 7 * DAY) {
if (delta < MINUTE) {
relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) });
} else if (delta < HOUR) {
relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) });
} else if (delta < DAY) {
relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) });
} else {
relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) });
}
} else if (date.getFullYear() === year) {
relativeTime = intl.formatDate(date, shortDateFormatOptions);
} else {
relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' });
}
const date = new Date(timestamp);
const relativeTime = timeAgoString(intl, date, this.state.now, year);
return (
<time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>

+ 32
- 13
app/javascript/packs/public.js View File

@ -22,15 +22,15 @@ window.addEventListener('message', e => {
function main() {
const { length } = require('stringz');
const IntlRelativeFormat = require('intl-relativeformat').default;
const IntlMessageFormat = require('intl-messageformat').default;
const { timeAgoString } = require('../mastodon/components/relative_timestamp');
const { delegate } = require('rails-ujs');
const emojify = require('../mastodon/features/emoji/emoji').default;
const { getLocale } = require('../mastodon/locales');
const { localeData } = getLocale();
const { messages } = getLocale();
const React = require('react');
const ReactDOM = require('react-dom');
localeData.forEach(IntlRelativeFormat.__addLocaleData);
const Rellax = require('rellax');
ready(() => {
const locale = document.documentElement.lang;
@ -43,8 +43,6 @@ function main() {
minute: 'numeric',
});
const relativeFormat = new IntlRelativeFormat(locale);
[].forEach.call(document.querySelectorAll('.emojify'), (content) => {
content.innerHTML = emojify(content.innerHTML);
});
@ -59,12 +57,16 @@ function main() {
[].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
const datetime = new Date(content.getAttribute('datetime'));
const now = new Date();
content.title = dateTimeFormat.format(datetime);
content.textContent = relativeFormat.format(datetime);
content.textContent = timeAgoString({
formatMessage: ({ id, defaultMessage }, values) => (new IntlMessageFormat(messages[id] || defaultMessage, locale)).format(values),
formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
}, datetime, now, datetime.getFullYear());
});
[].forEach.call(document.querySelectorAll('.logo-button'), (content) => {
[].forEach.call(document.querySelectorAll('.modal-button'), (content) => {
content.addEventListener('click', (e) => {
e.preventDefault();
window.open(e.target.href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
@ -82,6 +84,8 @@ function main() {
})
.catch(error => console.error(error));
}
new Rellax('.parallax', { speed: -1 });
});
delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
@ -106,15 +110,20 @@ function main() {
return false;
});
delegate(document, '.account_display_name', 'input', ({ target }) => {
delegate(document, '#account_display_name', 'input', ({ target }) => {
const nameCounter = document.querySelector('.name-counter');
const name = document.querySelector('.card .display-name strong');
if (nameCounter) {
nameCounter.textContent = 30 - length(target.value);
}
if (name) {
name.innerHTML = emojify(target.value);
}
});
delegate(document, '.account_note', 'input', ({ target }) => {
delegate(document, '#account_note', 'input', ({ target }) => {
const noteCounter = document.querySelector('.note-counter');
if (noteCounter) {
@ -123,7 +132,7 @@ function main() {
});
delegate(document, '#account_avatar', 'change', ({ target }) => {
const avatar = document.querySelector('.card.compact .avatar img');
const avatar = document.querySelector('.card .avatar img');
const [file] = target.files || [];
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
@ -131,11 +140,21 @@ function main() {
});
delegate(document, '#account_header', 'change', ({ target }) => {
const header = document.querySelector('.card.compact');
const header = document.querySelector('.card .card__img img');
const [file] = target.files || [];
const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
header.style.backgroundImage = `url(${url})`;
header.src = url;
});
delegate(document, '#account_locked', 'change', ({ target }) => {
const lock = document.querySelector('.card .display-name i');
if (target.checked) {
lock.style.display = 'inline';
} else {
lock.style.display = 'none';
}
});
}

+ 1
- 1
app/javascript/styles/application.scss View File

@ -10,7 +10,7 @@
@import 'mastodon/lists';
@import 'mastodon/footer';
@import 'mastodon/compact_header';
@import 'mastodon/landing_strip';
@import 'mastodon/widgets';
@import 'mastodon/forms';
@import 'mastodon/accounts';
@import 'mastodon/stream_entries';

+ 75
- 484
app/javascript/styles/mastodon/accounts.scss View File

@ -1,243 +1,100 @@
.card {
background-color: $base-shadow-color;
background-size: cover;
background-position: center;
border-radius: 4px 4px 0 0;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
overflow: hidden;
position: relative;
display: flex;
&::after {
background: rgba(darken($ui-base-color, 8%), 0.5);
& > a {
display: block;
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
}
@media screen and (max-width: 740px) {
border-radius: 0;
box-shadow: none;
}
.card__illustration {
padding: 60px 0;
position: relative;
flex: 1 1 auto;
display: flex;
justify-content: center;
align-items: center;
}
.card__bio {
max-width: 260px;
flex: 1 1 auto;
display: flex;
flex-direction: column;
justify-content: space-between;
background: rgba(darken($ui-base-color, 8%), 0.8);
position: relative;
z-index: 2;
}
&.compact {
padding: 30px 0;
border-radius: 4px;
.avatar {
margin-bottom: 0;
text-decoration: none;
color: inherit;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
img {
object-fit: cover;
}
@media screen and (max-width: $no-gap-breakpoint) {
box-shadow: none;
}
}
.name {
display: block;
font-size: 20px;
line-height: 18px * 1.5;
color: $primary-text-color;
padding: 10px 15px;
padding-bottom: 0;
font-weight: 500;
position: relative;
z-index: 2;
margin-bottom: 30px;
overflow: hidden;
text-overflow: ellipsis;
small {
display: block;
font-size: 14px;
color: $highlight-text-color;
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
.fa {
margin-left: 3px;
&:hover,
&:active,
&:focus {
.card__bar {
background: lighten($ui-base-color, 8%);
}
}
}
.avatar {
width: 120px;
margin: 0 auto;
&__img {
height: 130px;
position: relative;
z-index: 2;
background: darken($ui-base-color, 12%);
border-radius: 4px 4px 0 0;
img {
width: 120px;
height: 120px;
display: block;
border-radius: 120px;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
}
}
.roles {
margin-bottom: 30px;
padding: 0 15px;
}
.details-counters {
margin-top: 30px;
display: flex;
flex-direction: row;
width: 100%;
}
.counter {
width: 33.3%;
box-sizing: border-box;
flex: 0 0 auto;
color: $darker-text-color;
padding: 5px 10px 0;
margin-bottom: 10px;
border-right: 1px solid lighten($ui-base-color, 4%);
cursor: default;
text-align: center;
position: relative;
a {
display: block;
}
&:last-child {
border-right: 0;
}
&::after {
display: block;
content: "";
position: absolute;
bottom: -10px;
left: 0;
width: 100%;
border-bottom: 4px solid $ui-primary-color;
opacity: 0.5;
transition: all 400ms ease;
}
&.active {
&::after {
border-bottom: 4px solid $highlight-text-color;
opacity: 1;
}
}
&:hover {
&::after {
opacity: 1;
transition-duration: 100ms;
}
height: 100%;
margin: 0;
object-fit: cover;
border-radius: 4px 4px 0 0;
}
a {
text-decoration: none;
color: inherit;
@media screen and (max-width: 600px) {
height: 200px;
}
.counter-label {
font-size: 12px;
display: block;
margin-bottom: 5px;
}
.counter-number {
font-weight: 500;
font-size: 18px;
color: $primary-text-color;
font-family: 'mastodon-font-display', sans-serif;
@media screen and (max-width: $no-gap-breakpoint) {
display: none;
}
}
.bio {
font-size: 14px;
line-height: 18px;
padding: 0 15px;
color: $secondary-text-color;
}
@media screen and (max-width: 480px) {
display: block;
&__bar {
position: relative;
padding: 15px;
display: flex;
justify-content: flex-start;
align-items: center;
background: lighten($ui-base-color, 4%);
border-radius: 0 0 4px 4px;
.card__bio {
max-width: none;
@media screen and (max-width: $no-gap-breakpoint) {
border-radius: 0;
}
.name,
.roles {
text-align: center;
margin-bottom: 15px;
}
.avatar {
flex: 0 0 auto;
width: 48px;
height: 48px;
padding-top: 2px;
.bio {
margin-bottom: 15px;
img {
width: 100%;
height: 100%;
display: block;
margin: 0;
border-radius: 4px;
background: darken($ui-base-color, 8%);
}
}
}
}
.card,
.account-grid-card {
.controls {
position: absolute;
top: 15px;
left: 15px;
z-index: 2;
.icon-button {
color: rgba($white, 0.8);
text-decoration: none;
font-size: 13px;
line-height: 13px;
font-weight: 500;
.fa {
font-weight: 400;
margin-right: 5px;
.display-name {
margin-left: 15px;
text-align: left;
strong {
font-size: 15px;
color: $primary-text-color;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
}
&:hover,
&:active,
&:focus {
color: $white;
span {
display: block;
font-size: 14px;
color: $darker-text-color;
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
.account-grid-card .controls {
left: auto;
right: 15px;
}
.pagination {
padding: 30px 0;
text-align: center;
@ -314,289 +171,23 @@
}
}
.accounts-grid {
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
background: darken($simple-background-color, 8%);
border-radius: 0 0 4px 4px;
padding: 20px 5px;
padding-bottom: 10px;
overflow: hidden;
display: flex;
flex-wrap: wrap;
z-index: 2;
position: relative;
&.empty img {
position: absolute;
opacity: 0.2;
height: 200px;
left: 0;
bottom: 0;
pointer-events: none;
}
@media screen and (max-width: 740px) {
border-radius: 0;
box-shadow: none;
}
.account-grid-card {
box-sizing: border-box;
width: 335px;
background: $simple-background-color;
border-radius: 4px;
color: $inverted-text-color;
margin: 0 5px 10px;
position: relative;
@media screen and (max-width: 740px) {
width: calc(100% - 10px);
}
.account-grid-card__header {
overflow: hidden;
height: 100px;
border-radius: 4px 4px 0 0;
background-color: lighten($inverted-text-color, 4%);
background-size: cover;
background-position: center;
position: relative;
&::after {
background: rgba(darken($ui-base-color, 8%), 0.5);
display: block;
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
}
}
.account-grid-card__avatar {
box-sizing: border-box;
padding: 15px;
position: absolute;
z-index: 2;
top: 100px - (40px + 2px);
left: -2px;
}
.avatar {
width: 80px;
height: 80px;
img {
display: block;
width: 80px;
height: 80px;
border-radius: 80px;
border: 2px solid $simple-background-color;
background: $simple-background-color;
}
}
.name {
padding: 15px;
padding-top: 10px;
padding-left: 15px + 80px + 15px;
a {
display: block;
color: $inverted-text-color;
text-decoration: none;
text-overflow: ellipsis;
overflow: hidden;
font-weight: 500;
&:hover {
.display_name {
text-decoration: underline;
}
}
}
}
.display_name {
font-size: 16px;
display: block;
text-overflow: ellipsis;
overflow: hidden;
}
.username {
color: $lighter-text-color;
font-size: 14px;
font-weight: 400;
}
.account__header__content {
padding: 10px 15px;
padding-top: 15px;
color: $lighter-text-color;
word-wrap: break-word;
overflow: hidden;
text-overflow: ellipsis;
height: 5.5em;
position: relative;
&::after {
display: block;
content: "";
width: 100%;
height: 100px;
position: absolute;
bottom: 0;
background: linear-gradient(to bottom, rgba($simple-background-color, 0.01) 0%, rgba($simple-background-color, 1) 100%);
left: 0;
border-radius: 0 0 4px 4px;
pointer-events: none;
}
}
}
}
.nothing-here {
width: 100%;
display: block;
background: $ui-base-color;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
color: $light-text-color;
font-size: 14px;
font-weight: 500;
text-align: center;
padding: 130px 0;
padding-top: 125px;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
cursor: default;
}
.account-card {
border-radius: 4px;
text-align: left;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
background: $simple-background-color;
&__header {
background: $base-shadow-color;
background-size: cover;
background-position: center center;
height: 90px;
border-radius: 4px 4px 0 0;
}
padding: 20px;
min-height: 30vh;
& > .detailed-status__display-name {
display: block;
overflow: hidden;
display: flex;
align-items: center;
padding: 10px;
&:last-child {
margin-bottom: 0;
}
& > div:first-child {
flex: 0 0 auto;
margin-right: 10px;
width: 48px;
height: 48px;
}
.avatar {
display: block;
border-radius: 4px;
margin: 0;
}
.display-name {
flex: 1 0 auto;
display: block;
max-width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: default;
& > .detailed-status__display-name {
margin-bottom: 0;
}
strong {
font-weight: 500;
color: $ui-base-color;
@each $lang in $cjk-langs {
&:lang(#{$lang}) {
font-weight: 700;
}
}
}
span {
font-size: 14px;
color: $light-text-color;
}
}
&:hover {
.display-name {
strong {
text-decoration: none;
}
}
}
}
.counter {
box-sizing: border-box;
flex: 0 0 auto;
color: $light-text-color;
padding: 0 10px;
cursor: default;
text-align: center;
position: relative;
line-height: 24px;
.counter-label {
font-size: 12px;
display: block;
text-transform: uppercase;
}
.counter-number {
font-weight: 500;
font-size: 16px;
color: $inverted-text-color;
font-family: 'mastodon-font-display', sans-serif;
}
}
}
.activity-stream-tabs {
background: $simple-background-color;
border-bottom: 1px solid $ui-secondary-color;
position: relative;
z-index: 2;
a {
display: inline-block;
padding: 15px;
text-decoration: none;
color: $highlight-text-color;
text-transform: uppercase;
font-weight: 500;
&:hover,
&:active,
&:focus {
color: lighten($highlight-text-color, 8%);
}
&.active {
color: $inverted-text-color;
cursor: default;
}
&--under-tabs {
border-radius: 0 0 4px 4px;
}
}
@ -629,14 +220,14 @@
padding: 0;
margin: 15px -15px -15px;
border: 0 none;
border-top: 1px solid lighten($ui-base-color, 4%);
border-bottom: 1px solid lighten($ui-base-color, 4%);
border-top: 1px solid lighten($ui-base-color, 12%);
border-bottom: 1px solid lighten($ui-base-color, 12%);
font-size: 14px;
line-height: 20px;
dl {
display: flex;
border-bottom: 1px solid lighten($ui-base-color, 4%);
border-bottom: 1px solid lighten($ui-base-color, 12%);
}
dt,

+ 2
- 3
app/javascript/styles/mastodon/basics.scss View File

@ -1,13 +1,12 @@
body {
font-family: 'mastodon-font-sans-serif', sans-serif;
background: $ui-base-color;
background: darken($ui-base-color, 8%);
background-size: cover;
background-attachment: fixed;
font-size: 13px;
line-height: 18px;
font-weight: 400;
color: $primary-text-color;
padding-bottom: 20px;
text-rendering: optimizelegibility;
font-feature-settings: "kern";
text-size-adjust: none;
@ -52,7 +51,7 @@ body {
}
&.embed {
background: transparent;
background: lighten($ui-base-color, 4%);
margin: 0;
padding-bottom: 0;

+ 12
- 0
app/javascript/styles/mastodon/components.scss View File

@ -946,6 +946,18 @@
background: lighten($ui-base-color, 4%);
padding: 14px 10px;
&--flex {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: flex-start;
.status__content,
.detailed-status__meta {
flex: 100%;
}
}
.status__content {
font-size: 19px;
line-height: 24px;

+ 573
- 0
app/javascript/styles/mastodon/containers.scss View File

@ -118,3 +118,576 @@
margin-left: 8px;
}
}
.public-layout {
@media screen and (max-width: $no-gap-breakpoint) {
padding-top: 48px;
}
.container {
max-width: 960px;
@media screen and (max-width: $no-gap-breakpoint) {
padding: 0;
}
}
.header {
background: lighten($ui-base-color, 8%);
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
border-radius: 4px;
height: 48px;
margin: 10px 0;
display: flex;
align-items: stretch;
justify-content: center;
flex-wrap: nowrap;
overflow: hidden;
@media screen and (max-width: $no-gap-breakpoint) {
position: fixed;
width: 100%;
top: 0;
left: 0;
margin: 0;
border-radius: 0;
box-shadow: none;
z-index: 110;
}
& > div {
flex: 1 1 33.3%;
min-height: 1px;
}
.nav-left {
display: flex;
align-items: stretch;
justify-content: flex-start;
flex-wrap: nowrap;
}
.nav-center {
display: flex;
align-items: stretch;
justify-content: center;
flex-wrap: nowrap;
}
.nav-right {
display: flex;
align-items: stretch;
justify-content: flex-end;
flex-wrap: nowrap;
}
.brand {
display: block;
padding: 15px;
img {
display: block;
height: 18px;
width: auto;
position: relative;
bottom: -2px;
@media screen and (max-width: $no-gap-breakpoint) {
height: 20px;
}
}
&:hover,
&:focus,
&:active {
background: lighten($ui-base-color, 12%);
}
}
.nav-link {
display: flex;
align-items: center;
padding: 0 1rem;
font-size: 12px;
font-weight: 500;
text-decoration: none;
color: $darker-text-color;
white-space: nowrap;
text-align: center;
&:hover,
&:focus,
&:active {
text-decoration: underline;
color: $primary-text-color;
}
}
.nav-button {
background: lighten($ui-base-color, 16%);
margin: 8px;
margin-left: 0;
border-radius: 4px;
&:hover,
&:focus,
&:active {
text-decoration: none;
background: lighten($ui-base-color, 20%);
}
}
}
$no-columns-breakpoint: 600px;
.grid {
display: grid;
grid-gap: 10px;
grid-template-columns: minmax(300px, 3fr) minmax(298px, 1fr);
grid-auto-columns: 25%;
grid-auto-rows: max-content;
.column-0 {
grid-row: 1;
grid-column: 1;
}
.column-1 {
grid-row: 1;
grid-column: 2;
}
@media screen and (max-width: $no-columns-breakpoint) {
grid-template-columns: 100%;
grid-gap: 0;
.column-1 {
display: none;
}
}
}
.public-account-header {
overflow: hidden;
margin-bottom: 10px;
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
&__image {
border-radius: 4px 4px 0 0;
overflow: hidden;
height: 300px;
position: relative;
background: darken($ui-base-color, 12%);
&::after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
box-shadow: inset 0 -1px 1px 1px rgba($base-shadow-color, 0.15);
top: 0;
left: 0;
}
img {
object-fit: cover;
display: block;
width: 100%;
height: 100%;
margin: 0;
border-radius: 4px 4px 0 0;
}
@media screen and (max-width: 600px) {
height: 200px;
}
}
@media screen and (max-width: $no-gap-breakpoint) {
margin-bottom: 0;
box-shadow: none;
&__image::after {
display: none;
}
&__image,
&__image img {
border-radius: 0;
}
}
&__bar {
position: relative;
margin-top: -80px;
display: flex;
justify-content: flex-start;
&::before {
content: "";
display: block;
background: lighten($ui-base-color, 4%);
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60px;
border-radius: 0 0 4px 4px;
z-index: -1;
}
.avatar {
display: block;
width: 120px;
height: 120px;
padding-left: 20px - 4px;
flex: 0 0 auto;
img {
display: block;
width: 100%;
height: 100%;
margin: 0;
border-radius: 50%;
border: 4px solid lighten($ui-base-color, 4%);
background: darken($ui-base-color, 8%);
}
}
@media screen and (max-width: 600px) {
margin-top: 0;
background: lighten($ui-base-color, 4%);
border-radius: 0 0 4px 4px;
padding: 5px;
&::before {
display: none;
}
.avatar {
width: 48px;
height: 48px;
padding: 7px 0;
padding-left: 10px;
img {
border: 0;
border-radius: 4px;
}
@media screen and (max-width: 360px) {
display: none;
}
}
}
@media screen and (max-width: $no-gap-breakpoint) {
border-radius: 0;
}
@media screen and (max-width: $no-columns-breakpoint) {
flex-wrap: wrap;
}
}
&__tabs {
flex: 1 1 auto;
margin-left: 20px;
&__name {
padding-top: 20px;
padding-bottom: 8px;
h1 {
font-size: 20px;
line-height: 18px * 1.5;
color: $primary-text-color;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-shadow: 1px 1px 1px $base-shadow-color;
small {
display: block;
font-size: 14px;
color: $primary-text-color;
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
@media screen and (max-width: 600px) {
margin-left: 15px;
display: flex;
justify-content: space-between;
align-items: center;
&__name {
padding-top: 0;
padding-bottom: 0;
h1 {
font-size: 16px;
line-height: 24px;
text-shadow: none;
small {
color: $darker-text-color;
}
}
}
}
&__tabs {
display: flex;
justify-content: flex-start;
align-items: stretch;
height: 58px;
.details-counters {
display: flex;
flex-direction: row;
min-width: 300px;
}
@media screen and (max-width: $no-columns-breakpoint) {
.details-counters {
display: none;
}
}
.counter {
width: 33.3%;
box-sizing: border-box;
flex: 0 0 auto;
color: $darker-text-color;
padding: 10px;
border-right: 1px solid lighten($ui-base-color, 4%);
cursor: default;
text-align: center;
position: relative;
a {
display: block;
}
&:last-child {
border-right: 0;
}
&::after {
display: block;
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
border-bottom: 4px solid $ui-primary-color;
opacity: 0.5;
transition: all 400ms ease;
}
&.active {
&::after {
border-bottom: 4px solid $highlight-text-color;
opacity: 1;
}
}
&:hover {
&::after {
opacity: 1;
transition-duration: 100ms;
}
}
a {
text-decoration: none;
color: inherit;
}
.counter-label {
font-size: 12px;
display: block;
}
.counter-number {
font-weight: 500;
font-size: 18px;
margin-bottom: 5px;
color: $primary-text-color;
font-family: 'mastodon-font-display', sans-serif;
}
}
.spacer {
flex: 1 1 auto;
height: 1px;
}
&__buttons {
padding: 7px 8px;
}
}
}
&__extra {
display: none;
margin-top: 4px;
.public-account-bio {
border-radius: 0;
box-shadow: none;
background: transparent;
margin: 0 -5px;
.account__header__fields {
border-top: 1px solid lighten($ui-base-color, 12%);
}
.roles {
display: none;
}
}
&__links {
margin-top: -15px;
font-size: 14px;
color: $darker-text-color;
a {
display: inline-block;
color: $darker-text-color;
text-decoration: none;
padding: 15px;
strong {
font-weight: 700;
color: $primary-text-color;
}
}
}
@media screen and (max-width: $no-columns-breakpoint) {
display: block;
flex: 100%;
}
}
}
.account__section-headline {
border-radius: 4px 4px 0 0;
@media screen and (max-width: $no-gap-breakpoint) {
border-radius: 0;
}
}
.detailed-status__meta {
margin-top: 25px;
}
.public-account-bio {
background: lighten($ui-base-color, 8%);
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
border-radius: 4px;
overflow: hidden;
margin-bottom: 10px;
@media screen and (max-width: $no-gap-breakpoint) {
box-shadow: none;
margin-bottom: 0;
border-radius: 0;
}
.account__header__fields {
margin: 0;
border-top: 0;
a {
color: lighten($ui-highlight-color, 8%);
}
}
.account__header__content {
padding: 20px;
padding-bottom: 0;
color: $primary-text-color;
}
&__extra,
.roles {
padding: 20px;
font-size: 14px;
color: $darker-text-color;
}
.roles {
padding-bottom: 0;
}
}
.static-icon-button {
color: $action-button-color;
font-size: 18px;
& > span {
font-size: 14px;
font-weight: 500;
}
}
.card-grid {
display: flex;
flex-wrap: wrap;
min-width: 100%;
margin: 0 -5px;
& > div {
box-sizing: border-box;
flex: 1 0 auto;
width: 300px;
padding: 0 5px;
margin-bottom: 10px;
max-width: 33.333%;
@media screen and (max-width: 900px) {
max-width: 50%;
}
@media screen and (max-width: 600px) {
max-width: 100%;
}
}
@media screen and (max-width: $no-gap-breakpoint) {
margin: 0;
border-top: 1px solid lighten($ui-base-color, 8%);
& > div {
width: 100%;
padding: 0;
margin-bottom: 0;
border-bottom: 1px solid lighten($ui-base-color, 8%);
&:last-child {
border-bottom: 0;
}
.card__bar {
background: $ui-base-color;
&:hover,
&:active,
&:focus {
background: lighten($ui-base-color, 4%);
}
}
}
}
}
}

+ 129
- 28
app/javascript/styles/mastodon/footer.scss View File

@ -1,39 +1,140 @@
.footer {
text-align: center;
margin-top: 30px;
padding-bottom: 60px;
font-size: 12px;
color: $darker-text-color;
.footer__domain {
font-weight: 500;
a {
color: inherit;
text-decoration: none;
.public-layout {
.footer {
text-align: left;
padding-top: 20px;
padding-bottom: 60px;
font-size: 12px;
color: lighten($ui-base-color, 34%);
@media screen and (max-width: $no-gap-breakpoint) {
padding-left: 20px;
padding-right: 20px;
}
}
.powered-by,
.single-user-login {
font-weight: 400;
.grid {
display: grid;
grid-gap: 10px;
grid-template-columns: 1fr 1fr 2fr 1fr 1fr;
.column-0 {
grid-column: 1;
grid-row: 1;
min-width: 0;
}
.column-1 {
grid-column: 2;
grid-row: 1;
min-width: 0;
}
.column-2 {
grid-column: 3;
grid-row: 1;
min-width: 0;
text-align: center;
h4 a