Redesign landing page (#10232)

master
Eugen Rochko 5 years ago committed by GitHub
parent 6a8dc59eb8
commit 65fffeac3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 25
      app/controllers/about_controller.rb
  2. 34
      app/controllers/public_timelines_controller.rb
  3. 16
      app/controllers/tags_controller.rb
  4. 8
      app/helpers/home_helper.rb
  5. 12
      app/javascript/mastodon/containers/timeline_container.js
  6. 71
      app/javascript/mastodon/features/standalone/community_timeline/index.js
  7. 8
      app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
  8. 116
      app/javascript/mastodon/features/standalone/public_timeline/index.js
  9. 2
      app/javascript/mastodon/features/status/components/detailed_status.js
  10. 816
      app/javascript/styles/mastodon/about.scss
  11. 21
      app/javascript/styles/mastodon/forms.scss
  12. 5
      app/javascript/styles/mastodon/widgets.scss
  13. 8
      app/presenters/instance_presenter.rb
  14. 25
      app/views/about/_features.html.haml
  15. 15
      app/views/about/_forms.html.haml
  16. 16
      app/views/about/_links.html.haml
  17. 13
      app/views/about/_login.html.haml
  18. 20
      app/views/about/_registration.html.haml
  19. 208
      app/views/about/show.html.haml
  20. 33
      app/views/layouts/public.html.haml
  21. 14
      app/views/public_timelines/show.html.haml
  22. 1
      app/views/tags/show.html.haml
  23. 13
      config/locales/ar.yml
  24. 7
      config/locales/ast.yml
  25. 2
      config/locales/bg.yml
  26. 6
      config/locales/bn.yml
  27. 13
      config/locales/ca.yml
  28. 13
      config/locales/co.yml
  29. 13
      config/locales/cs.yml
  30. 13
      config/locales/cy.yml
  31. 13
      config/locales/da.yml
  32. 13
      config/locales/de.yml
  33. 13
      config/locales/el.yml
  34. 27
      config/locales/en.yml
  35. 13
      config/locales/eo.yml
  36. 13
      config/locales/es.yml
  37. 13
      config/locales/eu.yml
  38. 13
      config/locales/fa.yml
  39. 13
      config/locales/fi.yml
  40. 13
      config/locales/fr.yml
  41. 13
      config/locales/gl.yml
  42. 11
      config/locales/he.yml
  43. 2
      config/locales/hr.yml
  44. 11
      config/locales/hu.yml
  45. 11
      config/locales/id.yml
  46. 2
      config/locales/io.yml
  47. 13
      config/locales/it.yml
  48. 13
      config/locales/ja.yml
  49. 13
      config/locales/ka.yml
  50. 13
      config/locales/kk.yml
  51. 13
      config/locales/ko.yml
  52. 13
      config/locales/lt.yml
  53. 11
      config/locales/ms.yml
  54. 13
      config/locales/nl.yml
  55. 11
      config/locales/no.yml
  56. 13
      config/locales/oc.yml
  57. 13
      config/locales/pl.yml
  58. 13
      config/locales/pt-BR.yml
  59. 11
      config/locales/pt.yml
  60. 4
      config/locales/ro.yml
  61. 13
      config/locales/ru.yml
  62. 13
      config/locales/sk.yml
  63. 11
      config/locales/sl.yml
  64. 13
      config/locales/sq.yml
  65. 11
      config/locales/sr-Latn.yml
  66. 13
      config/locales/sr.yml
  67. 13
      config/locales/sv.yml
  68. 11
      config/locales/te.yml
  69. 2
      config/locales/th.yml
  70. 11
      config/locales/tr.yml
  71. 13
      config/locales/uk.yml
  72. 13
      config/locales/zh-CN.yml
  73. 13
      config/locales/zh-HK.yml
  74. 13
      config/locales/zh-TW.yml
  75. 1
      config/routes.rb
  76. 9
      spec/requests/localization_spec.rb
  77. 34
      spec/views/about/show.html.haml_spec.rb

@ -1,21 +1,17 @@
# frozen_string_literal: true
class AboutController < ApplicationController
before_action :set_body_classes
layout 'public'
before_action :set_instance_presenter, only: [:show, :more, :terms]
def show
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
@initial_state_json = serializable_resource.to_json
@hide_navbar = true
end
def more
render layout: 'public'
end
def more; end
def terms
render layout: 'public'
end
def terms; end
private
@ -28,15 +24,4 @@ class AboutController < ApplicationController
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
def set_body_classes
@body_classes = 'with-modals'
end
def initial_state_params
{
settings: { known_fediverse: Setting.show_known_fediverse_at_about_page },
token: current_session&.token,
}
end
end

@ -0,0 +1,34 @@
# frozen_string_literal: true
class PublicTimelinesController < ApplicationController
layout 'public'
before_action :check_enabled
before_action :set_body_classes
before_action :set_instance_presenter
def show
respond_to do |format|
format.html do
@initial_state_json = ActiveModelSerializers::SerializableResource.new(
InitialStatePresenter.new(settings: { known_fediverse: Setting.show_known_fediverse_at_about_page }, token: current_session&.token),
serializer: InitialStateSerializer
).to_json
end
end
end
private
def check_enabled
raise ActiveRecord::RecordNotFound unless Setting.timeline_preview
end
def set_body_classes
@body_classes = 'with-modals'
end
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
end

@ -13,8 +13,10 @@ class TagsController < ApplicationController
respond_to do |format|
format.html do
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
@initial_state_json = serializable_resource.to_json
@initial_state_json = ActiveModelSerializers::SerializableResource.new(
InitialStatePresenter.new(settings: {}, token: current_session&.token),
serializer: InitialStateSerializer
).to_json
end
format.rss do
@ -25,8 +27,7 @@ class TagsController < ApplicationController
end
format.json do
@statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local])
.paginate_by_max_id(PAGE_SIZE, params[:max_id])
@statuses = HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, params[:local]).paginate_by_max_id(PAGE_SIZE, params[:max_id])
@statuses = cache_collection(@statuses, Status)
render json: collection_presenter,
@ -55,11 +56,4 @@ class TagsController < ApplicationController
items: @statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) }
)
end
def initial_state_params
{
settings: {},
token: current_session&.token,
}
end
end

@ -56,4 +56,12 @@ module HomeHelper
'emojify'
end
end
def optional_link_to(condition, path, options = {}, &block)
if condition
link_to(path, options, &block)
else
content_tag(:div, &block)
end
end
end

@ -7,7 +7,6 @@ import { hydrateStore } from '../actions/store';
import { IntlProvider, addLocaleData } from 'react-intl';
import { getLocale } from '../locales';
import PublicTimeline from '../features/standalone/public_timeline';
import CommunityTimeline from '../features/standalone/community_timeline';
import HashtagTimeline from '../features/standalone/hashtag_timeline';
import ModalContainer from '../features/ui/containers/modal_container';
import initialState from '../initial_state';
@ -26,24 +25,22 @@ export default class TimelineContainer extends React.PureComponent {
static propTypes = {
locale: PropTypes.string.isRequired,
hashtag: PropTypes.string,
showPublicTimeline: PropTypes.bool.isRequired,
local: PropTypes.bool,
};
static defaultProps = {
showPublicTimeline: initialState.settings.known_fediverse,
local: !initialState.settings.known_fediverse,
};
render () {
const { locale, hashtag, showPublicTimeline } = this.props;
const { locale, hashtag, local } = this.props;
let timeline;
if (hashtag) {
timeline = <HashtagTimeline hashtag={hashtag} />;
} else if (showPublicTimeline) {
timeline = <PublicTimeline />;
} else {
timeline = <CommunityTimeline />;
timeline = <PublicTimeline local={local} />;
}
return (
@ -51,6 +48,7 @@ export default class TimelineContainer extends React.PureComponent {
<Provider store={store}>
<Fragment>
{timeline}
{ReactDOM.createPortal(
<ModalContainer />,
document.getElementById('modal-container'),

@ -1,71 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import StatusListContainer from '../../ui/containers/status_list_container';
import { expandCommunityTimeline } from '../../../actions/timelines';
import Column from '../../../components/column';
import ColumnHeader from '../../../components/column_header';
import { defineMessages, injectIntl } from 'react-intl';
import { connectCommunityStream } from '../../../actions/streaming';
const messages = defineMessages({
title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
});
export default @connect()
@injectIntl
class CommunityTimeline extends React.PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleHeaderClick = () => {
this.column.scrollTop();
}
setRef = c => {
this.column = c;
}
componentDidMount () {
const { dispatch } = this.props;
dispatch(expandCommunityTimeline());
this.disconnect = dispatch(connectCommunityStream());
}
componentWillUnmount () {
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
}
}
handleLoadMore = maxId => {
this.props.dispatch(expandCommunityTimeline({ maxId }));
}
render () {
const { intl } = this.props;
return (
<Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='users'
title={intl.formatMessage(messages.title)}
onClick={this.handleHeaderClick}
/>
<StatusListContainer
timelineId='community'
onLoadMore={this.handleLoadMore}
scrollKey='standalone_public_timeline'
trackScroll={false}
/>
</Column>
);
}
}

@ -2,13 +2,13 @@ import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { expandHashtagTimeline } from '../../../actions/timelines';
import { connectHashtagStream } from '../../../actions/streaming';
import { expandHashtagTimeline } from 'mastodon/actions/timelines';
import { connectHashtagStream } from 'mastodon/actions/streaming';
import Masonry from 'react-masonry-infinite';
import { List as ImmutableList } from 'immutable';
import DetailedStatusContainer from '../../status/containers/detailed_status_container';
import DetailedStatusContainer from 'mastodon/features/status/containers/detailed_status_container';
import { debounce } from 'lodash';
import LoadingIndicator from '../../../components/loading_indicator';
import LoadingIndicator from 'mastodon/components/loading_indicator';
const mapStateToProps = (state, { hashtag }) => ({
statusIds: state.getIn(['timelines', `hashtag:${hashtag}`, 'items'], ImmutableList()),

@ -1,42 +1,59 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import StatusListContainer from '../../ui/containers/status_list_container';
import { expandPublicTimeline } from '../../../actions/timelines';
import Column from '../../../components/column';
import ColumnHeader from '../../../components/column_header';
import { defineMessages, injectIntl } from 'react-intl';
import { connectPublicStream } from '../../../actions/streaming';
const messages = defineMessages({
title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
});
export default @connect()
@injectIntl
import ImmutablePropTypes from 'react-immutable-proptypes';
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
import Masonry from 'react-masonry-infinite';
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import DetailedStatusContainer from 'mastodon/features/status/containers/detailed_status_container';
import { debounce } from 'lodash';
import LoadingIndicator from 'mastodon/components/loading_indicator';
const mapStateToProps = (state, { local }) => {
const timeline = state.getIn(['timelines', local ? 'community' : 'public'], ImmutableMap());
return {
statusIds: timeline.get('items', ImmutableList()),
isLoading: timeline.get('isLoading', false),
hasMore: timeline.get('hasMore', false),
};
};
export default @connect(mapStateToProps)
class PublicTimeline extends React.PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
statusIds: ImmutablePropTypes.list.isRequired,
isLoading: PropTypes.bool.isRequired,
hasMore: PropTypes.bool.isRequired,
local: PropTypes.bool,
};
handleHeaderClick = () => {
this.column.scrollTop();
componentDidMount () {
this._connect();
}
setRef = c => {
this.column = c;
componentDidUpdate (prevProps) {
if (prevProps.local !== this.props.local) {
this._disconnect();
this._connect();
}
}
componentDidMount () {
const { dispatch } = this.props;
componentWillUnmount () {
this._disconnect();
}
dispatch(expandPublicTimeline());
this.disconnect = dispatch(connectPublicStream());
_connect () {
const { dispatch, local } = this.props;
dispatch(local ? expandCommunityTimeline() : expandPublicTimeline());
this.disconnect = dispatch(local ? connectCommunityStream() : connectPublicStream());
}
componentWillUnmount () {
_disconnect () {
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
@ -44,27 +61,48 @@ class PublicTimeline extends React.PureComponent {
}
handleLoadMore = maxId => {
this.props.dispatch(expandPublicTimeline({ maxId }));
const { dispatch, local } = this.props;
dispatch(local ? expandCommunityTimeline({ maxId }) : expandPublicTimeline({ maxId }));
}
setRef = c => {
this.masonry = c;
}
handleHeightChange = debounce(() => {
if (!this.masonry) {
return;
}
this.masonry.forcePack();
}, 50)
render () {
const { intl } = this.props;
const { statusIds, hasMore, isLoading } = this.props;
const sizes = [
{ columns: 1, gutter: 0 },
{ mq: '415px', columns: 1, gutter: 10 },
{ mq: '640px', columns: 2, gutter: 10 },
{ mq: '960px', columns: 3, gutter: 10 },
{ mq: '1255px', columns: 3, gutter: 10 },
];
const loader = (isLoading && statusIds.isEmpty()) ? <LoadingIndicator key={0} /> : undefined;
return (
<Column ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='globe'
title={intl.formatMessage(messages.title)}
onClick={this.handleHeaderClick}
/>
<StatusListContainer
timelineId='public'
onLoadMore={this.handleLoadMore}
scrollKey='standalone_public_timeline'
trackScroll={false}
/>
</Column>
<Masonry ref={this.setRef} className='statuses-grid' hasMore={hasMore} loadMore={this.handleLoadMore} sizes={sizes} loader={loader}>
{statusIds.map(statusId => (
<div className='statuses-grid__item' key={statusId}>
<DetailedStatusContainer
id={statusId}
compact
measureHeight
onHeightChange={this.handleHeightChange}
/>
</div>
)).toArray()}
</Masonry>
);
}

@ -23,7 +23,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
};
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
status: ImmutablePropTypes.map,
onOpenMedia: PropTypes.func.isRequired,
onOpenVideo: PropTypes.func.isRequired,
onToggleHidden: PropTypes.func.isRequired,

@ -193,6 +193,7 @@ $small-breakpoint: 960px;
}
strong {
font-family: $font-display, sans-serif;
font-weight: 500;
font-size: 32px;
line-height: 48px;
@ -280,168 +281,6 @@ $small-breakpoint: 960px;
}
.landing-page {
.grid {
display: grid;
grid-gap: 10px;
grid-template-columns: 1fr 2fr;
grid-auto-columns: 25%;
grid-auto-rows: max-content;
.column-0 {
display: none;
}
.column-1 {
grid-column: 1;
grid-row: 1;
}
.column-2 {
grid-column: 2;
grid-row: 1;
}
.column-3 {
grid-column: 3;
grid-row: 1 / 3;
}
.column-4 {
grid-column: 1 / 3;
grid-row: 2;
}
}
@media screen and (max-width: $small-breakpoint) {
.grid {
grid-template-columns: 40% 60%;
.column-0 {
display: none;
}
.column-1 {
grid-column: 1;
grid-row: 1;
&.non-preview .landing-page__forms {
height: 100%;
}
}
.column-2 {
grid-column: 2;
grid-row: 1 / 3;
&.non-preview {
grid-column: 2;
grid-row: 1;
}
}
.column-3 {
grid-column: 1;
grid-row: 2 / 4;
}
.column-4 {
grid-column: 2;
grid-row: 3;
&.non-preview {
grid-column: 1 / 3;
grid-row: 2;
}
}
}
}
@media screen and (max-width: $column-breakpoint) {
.grid {
grid-template-columns: 100%;
.column-0 {
display: block;
grid-column: 1;
grid-row: 1;
}
.column-1 {
grid-column: 1;
grid-row: 3;
.brand {
display: none;
}
}
.column-2 {
grid-column: 1;
grid-row: 2;
.landing-page__logo,
.landing-page__call-to-action {
display: none;
}
&.non-preview {
grid-column: 1;
grid-row: 2;
}
}
.column-3 {
grid-column: 1;
grid-row: 5;
}
.column-4 {
grid-column: 1;
grid-row: 4;
&.non-preview {
grid-column: 1;
grid-row: 4;
}
}
}
}
.column-flex {
display: flex;
flex-direction: column;
}
.separator-or {
position: relative;
margin: 40px 0;
text-align: center;
&::before {
content: "";
display: block;
width: 100%;
height: 0;
border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
position: absolute;
top: 50%;
left: 0;
}
span {
display: inline-block;
background: $ui-base-color;
font-size: 12px;
font-weight: 500;
color: $darker-text-color;
text-transform: uppercase;
position: relative;
z-index: 1;
padding: 0 8px;
cursor: default;
}
}
p,
li {
font-family: $font-sans-serif, sans-serif;
@ -458,28 +297,6 @@ $small-breakpoint: 960px;
}
}
.closed-registrations-message {
margin-top: 20px;
&,
p {
text-align: center;
font-size: 12px;
line-height: 18px;
color: $darker-text-color;
margin-bottom: 0;
a {
color: $highlight-text-color;
text-decoration: underline;
}
}
p:last-child {
margin-bottom: 0;
}
}
em {
display: inline;
margin: 0;
@ -593,187 +410,6 @@ $small-breakpoint: 960px;
}
}
.container-alt {
width: 100%;
box-sizing: border-box;
max-width: 800px;
margin: 0 auto;
word-wrap: break-word;
}
.header-wrapper {
padding-top: 15px;
background: $ui-base-color;
background: linear-gradient(150deg, lighten($ui-base-color, 8%), $ui-base-color);
position: relative;
&.compact {
background: $ui-base-color;
padding-bottom: 15px;
.hero .heading {
padding-bottom: 20px;
font-family: $font-sans-serif, sans-serif;
font-size: 16px;
font-weight: 400;
font-size: 16px;
line-height: 30px;
color: $darker-text-color;
a {
color: $highlight-text-color;
text-decoration: underline;
}
}
}
}
.brand {
a {
padding-left: 0;
padding-right: 0;
color: $white;
}
img {
height: 32px;
position: relative;
top: 4px;
left: -10px;
}
}
.header {
line-height: 30px;
overflow: hidden;
.container-alt {
display: flex;
justify-content: space-between;
}
.links {
position: relative;
z-index: 4;
a {
display: flex;
justify-content: center;
align-items: center;
color: $darker-text-color;
text-decoration: none;
padding: 12px 16px;
line-height: 32px;
font-family: $font-display, sans-serif;
font-weight: 500;
font-size: 14px;
&:hover {
color: $secondary-text-color;
}
}
ul {
list-style: none;
margin: 0;
li {
display: inline-block;
vertical-align: bottom;
margin: 0;
&:first-child a {
padding-left: 0;
}
&:last-child a {
padding-right: 0;
}
}
}
}
.hero {
margin-top: 50px;
align-items: center;
position: relative;
.heading {
position: relative;
z-index: 4;
padding-bottom: 150px;
}
.simple_form,
.closed-registrations-message {
background: darken($ui-base-color, 4%);
width: 280px;
padding: 15px 20px;
border-radius: 4px 4px 0 0;
line-height: initial;
position: relative;
z-index: 4;
.actions {
margin-bottom: 0;
button,
.button,
.block-button {
margin-bottom: 0;
}
}
}
.closed-registrations-message {
min-height: 330px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
}
}
.about-short {
background: darken($ui-base-color, 4%);
padding: 50px 0 30px;
font-family: $font-sans-serif, sans-serif;
font-size: 16px;
font-weight: 400;
font-size: 16px;
line-height: 30px;
color: $darker-text-color;
a {
color: $highlight-text-color;
text-decoration: underline;
}
}
&.alternative {
padding: 10px 0;
.brand {
text-align: center;
padding: 30px 0;
margin-bottom: 10px;
img {
position: static;
padding: 10px 0;
}
@media screen and (max-width: $small-breakpoint) {
padding: 15px 0;
}
@media screen and (max-width: $column-breakpoint) {
padding: 0;
margin-bottom: -10px;
}
}
}
&__information,
&__forms {
padding: 20px;
@ -967,353 +603,253 @@ $small-breakpoint: 960px;
}
}
&__forms {
height: 100%;
@media screen and (max-width: $small-breakpoint) {
height: auto;
}
@media screen and (max-width: $column-breakpoint) {
background: transparent;
box-shadow: none;
padding: 0 20px;
margin-top: 30px;
margin-bottom: 40px;
.separator-or {
span {
background: darken($ui-base-color, 8%);
}
@media screen and (max-width: 840px) {
.information-board {
.container-alt {
padding-right: 20px;
}
}
hr {
margin: 40px 0;
}
.button {
display: block;
}
.subtle-hint a {
text-decoration: none;
.panel {
position: static;
margin-top: 20px;
width: 100%;
border-radius: 4px;
&:hover,
&:focus,
&:active {
text-decoration: underline;
.panel-header {
text-align: center;
}
}
}
}
#mastodon-timeline {
display: flex;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
font-family: $font-sans-serif, sans-serif;
font-size: 13px;
line-height: 18px;
font-weight: 400;
color: $primary-text-color;
width: 100%;
flex: 1 1 auto;
overflow: hidden;
height: 100%;
.column-header {
color: inherit;
font-family: inherit;
font-size: 16px;
line-height: inherit;
font-weight: inherit;
margin: 0;
padding: 0;
}
.column {
padding: 0;
border-radius: 4px;
overflow: hidden;
width: 100%;
}
.scrollable {
height: 400px;
}
p {
font-size: inherit;
line-height: inherit;
font-weight: inherit;
color: $primary-text-color;
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
@media screen and (max-width: 675px) {
.header-wrapper {
padding-top: 0;
a {
color: $secondary-text-color;
text-decoration: none;
&.compact {
padding-bottom: 0;
}
}
.attachment-list__list {
margin-left: 0;
list-style: none;
li {
font-size: inherit;
line-height: inherit;
font-weight: inherit;
margin-bottom: 0;
a {
color: $dark-text-color;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
&.compact .hero .heading {
text-align: initial;
}
}
@media screen and (max-width: $column-breakpoint) {
display: none;
.header .container-alt,
.features .container-alt {
display: block;
}
}
&__features {
& > p {
padding-right: 60px;
}
.features-list {
margin: 40px 0;
margin-top: 30px;
}
&__action {
text-align: center;
}
.cta {
margin: 20px;
}
}
.features-list {
.features-list__row {
display: flex;
padding: 10px 0;
justify-content: space-between;
.visual {
flex: 0 0 auto;
display: flex;
align-items: center;
margin-left: 15px;
.landing {
margin-bottom: 100px;
.fa {
display: block;
color: $darker-text-color;
font-size: 48px;
}
}
@media screen and (max-width: 738px) {
margin-bottom: 0;
}
.text {
font-size: 16px;
line-height: 30px;
color: $darker-text-color;
&__brand {
display: flex;
justify-content: center;
align-items: center;
padding: 100px;
h6 {
font-size: inherit;
line-height: inherit;
margin-bottom: 0;
}
}
img {
height: 52px;
}
@media screen and (min-width: $small-breakpoint) {
display: grid;
grid-gap: 30px;
grid-template-columns: 1fr 1fr;
grid-auto-columns: 50%;
grid-auto-rows: max-content;
@media screen and (max-width: $no-gap-breakpoint) {
padding: 0;
margin-bottom: 30px;
}
}
.footer-links {
padding-bottom: 50px;
text-align: right;
color: $dark-text-color;
.directory {
margin-top: 30px;
background: transparent;
box-shadow: none;
border-radius: 0;
}
p {
font-size: 14px;
}
.hero-widget {
margin-top: 30px;
margin-bottom: 0;
a {
color: inherit;
text-decoration: underline;
h4 {
padding: 10px;
text-transform: uppercase;
font-weight: 700;
font-size: 13px;
color: $darker-text-color;
}
}
&__footer {
margin-top: 10px;
text-align: center;
color: $dark-text-color;
&__text {
border-radius: 0;
padding-bottom: 0;
}
p {
font-size: 14px;
&__footer {
background: $ui-base-color;
padding: 10px;
border-radius: 0 0 4px 4px;
display: flex;
a {
color: inherit;
text-decoration: underline;
&__column {
flex: 1 1 50%;
}
}
}
@media screen and (max-width: 840px) {
.container-alt {
padding: 0 20px;
}
.account {
padding: 10px 0;
border-bottom: 0;
.information-board {
.container-alt {
padding-right: 20px;
.account__display-name {
display: flex;
align-items: center;
}
.panel {
position: static;
margin-top: 20px;
width: 100%;
border-radius: 4px;
.panel-header {
text-align: center;
}
.account__avatar {
width: 44px;
height: 44px;
background-size: 44px 44px;
}
}
}
@media screen and (max-width: 675px) {
.header-wrapper {
padding-top: 0;
&__counter {
padding: 10px;
&.compact {
padding-bottom: 0;
strong {
font-family: $font-display, sans-serif;
font-size: 15px;
font-weight: 700;
display: block;
}
&.compact .hero .heading {
text-align: initial;
span {
font-size: 14px;
color: $darker-text-color;
}
}
}
.header .container-alt,
.features .container-alt {
display: block;
}
.header {
.links {
padding-top: 15px;
background: darken($ui-base-color, 4%);
.simple_form .user_agreement .label_input > label {
font-weight: 400;
color: $darker-text-color;
}
a {
padding: 12px 8px;
}
.simple_form p.lead {
color: $darker-text-color;
font-size: 15px;
line-height: 20px;
font-weight: 400;
margin-bottom: 25px;
}
.nav {
display: flex;
flex-flow: row wrap;
justify-content: space-around;
}
&__grid {
max-width: 960px;
margin: 0 auto;
display: grid;
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
grid-gap: 30px;
.brand img {
left: 0;
top: 0;
}
}
@media screen and (max-width: 738px) {
grid-template-columns: minmax(0, 100%);
grid-gap: 10px;
.hero {
margin-top: 30px;
padding: 0;
&__column-login {
grid-row: 1;
display: flex;
flex-direction: column;
.heading {
padding: 30px 20px;
text-align: center;
.box-widget {
order: 2;
flex: 0 0 auto;
}
.simple_form,
.closed-registrations-message {
background: darken($ui-base-color, 8%);
width: 100%;
border-radius: 0;
box-sizing: border-box;
.hero-widget {
margin-top: 0;
margin-bottom: 10px;
order: 1;
flex: 0 0 auto;
}
}
}
}
.cta {
margin: 20px;
}
&.tag-page {
@media screen and (max-width: $column-breakpoint) {
padding: 0;
.container {
padding: 0;
&__column-registration {
grid-row: 2;
}
#mastodon-timeline {
display: flex;
height: 100vh;
border-radius: 0;
.directory {
margin-top: 10px;
}
}
.grid {
@media screen and (min-width: $small-breakpoint) {
grid-template-columns: 33% 67%;
}
.column-2 {
grid-column: 2;
grid-row: 1;
}
}
@media screen and (max-width: $no-gap-breakpoint) {
grid-gap: 0;
.brand {
text-align: unset;
padding: 0;
.hero-widget {
display: block;
margin-bottom: 0;
box-shadow: none;
img {
height: 48px;
width: auto;
&__img,
&__img img,
&__footer {
border-radius: 0;
}
}
}
.cta {
margin: 0;
.button {
margin-right: 4px;
.hero-widget,
.box-widget,
.directory__tag {
border-bottom: 1px solid lighten($ui-base-color, 8%);
}
}
@media screen and (max-width: $column-breakpoint) {
.grid {
grid-gap: 0;
.directory {
margin-top: 0;
.column-1 {
grid-column: 1;
grid-row: 1;
}
&__tag {
margin-bottom: 0;
.column-2 {
display: none;
& > a,
& > div {
border-radius: 0;
box-shadow: none;
}
&:last-child {
border-bottom: 0;
}
}
}
}
}
}
.brand {
position: relative;
text-decoration: none;
}
.brand__tagline {
display: block;
position: absolute;
bottom: -10px;
left: 50px;
width: 300px;
color: $ui-primary-color;
text-decoration: none;
font-size: 14px;
@media screen and (max-width: $no-gap-breakpoint) {
position: static;
width: auto;
margin-top: 20px;
color: $dark-text-color;
}
}

@ -68,6 +68,17 @@ code {
top: 2px;
left: 0;
}
label a {
color: $highlight-text-color;
text-decoration: underline;
&:hover,
&:active,
&:focus {
text-decoration: none;
}