This commit is contained in:
mgabdev 2020-05-09 23:26:58 -04:00
parent b620cb1372
commit dcb0a2c74b
25 changed files with 223 additions and 184 deletions

@ -0,0 +1,24 @@
const AngleLeftIcon = ({
className = '',
size = '16px',
title = '',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={size}
height={size}
viewBox='0 0 64 64'
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d='M 25.835938 32.011719 L 49.777344 8.070312 C 50.4375 7.414062 50.800781 6.535156 50.800781 5.59375 C 50.800781 4.65625 50.4375 3.777344 49.777344 3.121094 L 47.679688 1.023438 C 47.019531 0.363281 46.140625 0 45.203125 0 C 44.265625 0 43.386719 0.363281 42.730469 1.023438 L 14.222656 29.53125 C 13.558594 30.191406 13.199219 31.074219 13.199219 32.011719 C 13.199219 32.953125 13.558594 33.835938 14.222656 34.5 L 42.703125 62.976562 C 43.359375 63.636719 44.238281 64 45.179688 64 C 46.117188 64 46.996094 63.636719 47.652344 62.976562 L 49.75 60.878906 C 51.117188 59.515625 51.117188 57.292969 49.75 55.929688 Z M 25.835938 32.011719' />
</g>
</svg>
)
export default AngleLeftIcon

@ -1,3 +1,4 @@
import { CX } from '../constants'
import Button from './button'
export default class BackButton extends PureComponent {
@ -7,7 +8,8 @@ export default class BackButton extends PureComponent {
}
static propTypes = {
classNames: PropTypes.string,
className: PropTypes.string,
icon: PropTypes.string,
iconClassName: PropTypes.string,
iconSize: PropTypes.string,
}
@ -26,18 +28,29 @@ export default class BackButton extends PureComponent {
render() {
const {
classNames,
className,
icon,
iconClassName,
iconSize,
} = this.props
const classes = CX(className, {
alignItemsCenter: 1,
bgTransparent: 1,
mr5: 1,
cursorPointer: 1,
outlineNone: 1,
default: 1,
justifyContentCenter: 1,
})
return (
<Button
noClasses
color='primary'
backgroundColor='none'
className={classNames || [_s.alignItemsCenter, _s.bgTransparent, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.default, _s.justifyContentCenter].join(' ')}
icon='arrow-left'
className={classes}
icon={icon || 'angle-left'}
iconSize={iconSize || '24px'}
iconClassName={iconClassName || [_s.mr5, _s.fillPrimary].join(' ')}
onClick={this.handleBackClick}

@ -33,6 +33,7 @@ class DisplayName extends ImmutablePureComponent {
noRelationship: PropTypes.bool,
noUsername: PropTypes.bool,
isComment: PropTypes.bool,
isCentered: PropTypes.bool,
}
updateOnProps = [
@ -44,6 +45,7 @@ class DisplayName extends ImmutablePureComponent {
'noRelationship',
'noUsername',
'isComment',
'isCentered',
]
mouseOverTimeout = null
@ -81,6 +83,7 @@ class DisplayName extends ImmutablePureComponent {
noRelationship,
isSmall,
isComment,
isCentered,
} = this.props
if (!account) return null
@ -91,6 +94,7 @@ class DisplayName extends ImmutablePureComponent {
alignItemsCenter: !isMultiline,
flexRow: !isMultiline,
cursorPointer: !noHover,
alignItemsCenter: isCentered,
})
const displayNameClasses = CX({
@ -124,9 +128,9 @@ class DisplayName extends ImmutablePureComponent {
})
const iconSize =
!!isLarge ? '19px' :
!!isComment ? '12px' :
!!isSmall ? '14px' : '15px'
!!isLarge ? 19 :
!!isComment ? 12 :
!!isSmall ? 14 : 15
const domain = account.get('acct').split('@')[1]
const isRemoteUser = !!domain
@ -157,20 +161,20 @@ class DisplayName extends ImmutablePureComponent {
onMouseLeave={noHover ? undefined : this.handleMouseLeave}
ref={this.setRef}
>
<span className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.maxWidth180PX, _s.maxWidth100PC].join(' ')}>
<span className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.maxWidth100PC].join(' ')}>
<bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}>
<strong
className={displayNameClasses}
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
/>
{
!noRelationship && account.get('locked') &&
<Icon id='lock-filled' size={iconSize} className={[_s.fillPrimary, _s.ml5].join(' ')} />
account.get('locked') &&
<Icon id='lock-filled' size={`${iconSize - 3}px`} className={[_s.fillPrimary, _s.ml5].join(' ')} />
}
</bdi>
{
account.get('is_verified') &&
<Icon id='verified' size={iconSize} className={_s.default} />
<Icon id='verified' size={`${iconSize}px`} className={[_s.ml5, _s.default].join(' ')} />
}
</span>
{

@ -1,5 +1,7 @@
export default class Dummy extends PureComponent {
render() {
return <div>{this.props.children}</div>
}
}

@ -1,53 +1,89 @@
import { withRouter } from 'react-router-dom'
import { me } from '../initial_state'
import { CX } from '../constants'
import Button from './button'
export default
@withRouter
class FooterBar extends PureComponent {
static contextTypes = {
router: PropTypes.object,
}
render() {
if (!me) return false
const noRouter = !this.context.router
const currentPathname = noRouter ? '' : this.context.router.route.location.pathname
const buttons = [
{
to: '/home',
icon: 'home',
title: 'Home',
active: currentPathname === '/home',
},
{
to: '/notifications',
icon: 'notifications',
title: 'Notifications',
active: currentPathname === '/notifications',
},
{
href: 'https://chat.gab.com',
icon: 'chat',
title: 'Chat',
},
{
href: 'https://trends.gab.com',
icon: 'trends',
title: 'Trends',
},
{
to: '/groups',
icon: 'group',
title: 'Groups',
active: currentPathname === '/groups',
},
]
return (
<div className={[_s.default, _s.z4, _s.heightMin58PX, _s.width100PC].join(' ')}>
<div className={[_s.default, _s.posFixed, _s.left0, _s.right0, _s.bottom0, _s.heightMin58PX, _s.width100PC, _s.bgPrimary, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.height100PC, _s.heightMin58PX, _s.footerChin, _s.justifyContentSpaceAround].join(' ')}>
<Button
backgroundColor='none'
color='secondary'
to='/home'
icon='home'
iconSize='20px'
/>
<Button
backgroundColor='none'
color='secondary'
to='/notifications'
icon='notifications'
iconSize='20px'
/>
<Button
backgroundColor='none'
color='secondary'
href='https://chat.gab.com'
icon='chat'
iconSize='20px'
/>
<Button
backgroundColor='none'
color='secondary'
href='https://trends.gab.com'
icon='trends'
iconSize='20px'
/>
<Button
backgroundColor='none'
color='secondary'
to='/groups'
icon='group'
iconSize='20px'
/>
{
buttons.map((props) => {
const classes = CX({
borderTop2PX: 1,
borderColorTransparent: !props.active,
borderColorBrand: props.active,
height100PC: 1,
heightMin58PX: 1,
px15: 1,
flexGrow1: 1,
alignItemsCenter: 1,
justifyContentCenter: 1,
})
const color = props.active ? 'brand' : 'secondary'
return (
<Button
isText
backgroundColor='none'
iconSize='20px'
color={color}
to={props.to}
icon={props.icon}
href={props.href}
title={props.title}
className={classes}
/>
)
})
}
</div>
</div>
</div>

@ -1,5 +1,6 @@
import AddIcon from '../assets/add_icon'
import AngleRightIcon from '../assets/angle_right_icon'
import AngleLeftIcon from '../assets/angle_left_icon'
import AppsIcon from '../assets/apps_icon'
import ArrowLeftIcon from '../assets/arrow_left_icon'
import ArrowRightIcon from '../assets/arrow_right_icon'
@ -74,6 +75,7 @@ import WebsiteIcon from '../assets/website_icon'
const ICONS = {
'add': AddIcon,
'angle-right': AngleRightIcon,
'angle-left': AngleLeftIcon,
'apps': AppsIcon,
'arrow-left': ArrowLeftIcon,
'arrow-right': ArrowRightIcon,

@ -1,30 +1,8 @@
import classNames from 'classnames';
import { LoadingBar } from 'react-redux-loading-bar';
import ZoomableImage from './zoomable_image';
// .image-loader {
// position: relative;
// @include flex(center, center, column);
// @include size(100%);
// &__preview-canvas {
// object-fit: contain;
// @include max-size($media-modal-media-max-width, $media-modal-media-max-height);
// @include background-image("", contain, center, repeat);
// }
// &--amorphous & {
// &__preview-canvas {
// display: none;
// }
// }
// .loading-bar {
// position: relative;
// }
// }
import { LoadingBar } from 'react-redux-loading-bar'
import {
CX,
} from '../constants'
import ZoomableImage from './zoomable_image'
export default class ImageLoader extends PureComponent {
@ -54,7 +32,7 @@ export default class ImageLoader extends PureComponent {
get canvasContext() {
if (!this.canvas) {
return null;
return null
}
this._canvasContext = this._canvasContext || this.canvas.getContext('2d');
@ -151,12 +129,39 @@ export default class ImageLoader extends PureComponent {
}
render () {
const { alt, src, width, height, onClick } = this.props;
const { loading } = this.state;
const { alt, src, width, height, onClick } = this.props
const { loading } = this.state
const className = classNames('image-loader', {
'image-loader--loading': loading,
'image-loader--amorphous': !this.hasSize(),
// .image-loader {
// position: relative;
// @include flex(center, center, column);
// @include size(100%);
// &__preview-canvas {
// object-fit: contain;
// @include max-size($media-modal-media-max-width, $media-modal-media-max-height);
// @include background-image("", contain, center, repeat);
// }
// &--amorphous & {
// &__preview-canvas {
// display: none;
// }
// }
// .loading-bar {
// position: relative;
// }
// }
const className = CX({
default: 1,
width100PC: 1,
height100PC: 1,
// 'image-loader--loading': loading,
// 'image-loader--amorphous': !this.hasSize(),
});
return (
@ -164,7 +169,7 @@ export default class ImageLoader extends PureComponent {
<LoadingBar loading={loading ? 1 : 0} className='loading-bar' style={{ width: this.state.width || width }} />
{loading ? (
<canvas
className='image-loader__preview-canvas'
className={[_s.default, _s.objectFitCover].join(' ')}
ref={this.setCanvasRef}
width={width}
height={height}

@ -148,14 +148,7 @@ class ModalBase extends PureComponent {
onClick={this.handleOnClose}
className={[_s.default, _s.posFixed, _s.alignItemsCenter, _s.justifyContentCenter, _s.z4, _s.width100PC, _s.height100PC, _s.top0, _s.rightAuto, _s.bottomAuto, _s.left0].join(' ')}
>
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
{children}
</Responsive>
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
<CardView>
{children}
</CardView>
</Responsive>
{children}
</div>
</Fragment>
}

@ -7,6 +7,7 @@ import Responsive from '../features/ui/util/responsive_component'
import ResponsiveClassesComponent from '../features/ui/util/responsive_classes_component'
import { CX } from '../constants'
import Avatar from './avatar'
import BackButton from './back_button'
import Button from './button'
import Heading from './heading'
import Icon from './icon'
@ -21,10 +22,6 @@ export default
@connect(mapStateToProps)
class NavigationBar extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
static propTypes = {
account: ImmutablePropTypes.map,
actions: PropTypes.array,
@ -33,18 +30,6 @@ class NavigationBar extends ImmutablePureComponent {
showBackBtn: PropTypes.bool,
}
historyBack = () => {
if (window.history && window.history.length === 1) {
this.context.router.history.push('/home')
} else {
this.context.router.history.goBack()
}
}
handleBackClick = () => {
this.historyBack()
}
handleProfileClick = () => {
}
@ -119,7 +104,7 @@ class NavigationBar extends ImmutablePureComponent {
{ /** Mobile */}
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
<div className={[_s.default, _s.width84PX, _s.pl10].join(' ')}>
<div className={[_s.default, _s.width84PX, _s.alignItemsStart, _s.pl10].join(' ')}>
{
!!account && !showBackBtn &&
<button
@ -132,15 +117,11 @@ class NavigationBar extends ImmutablePureComponent {
}
{
showBackBtn &&
<Button
noClasses
color='primary'
backgroundColor='none'
className={[_s.height53PX, _s.bgTransparent, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.default, _s.justifyContentCenter].join(' ')}
icon='arrow-left'
iconSize='32px'
<BackButton
className={_s.height53PX}
icon='angle-left'
iconSize='18px'
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
onClick={this.handleBackClick}
/>
}
</div>

@ -1,6 +1,5 @@
import { Fragment } from 'react'
import { defineMessages, injectIntl } from 'react-intl'
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { List as ImmutableList } from 'immutable'
@ -27,13 +26,8 @@ const mapStateToProps = (state, { account }) => {
}
}
const mapDispatchToProps = (dispatch) => ({
fetchSuggestions: () => dispatch(fetchSuggestions()),
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))),
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@connect(mapStateToProps, null)
@injectIntl
class ProfileInfoPanel extends ImmutablePureComponent {
@ -44,10 +38,6 @@ class ProfileInfoPanel extends ImmutablePureComponent {
intl: PropTypes.object.isRequired,
}
componentDidMount() {
this.props.fetchSuggestions()
}
render() {
const {
intl,

@ -36,24 +36,8 @@ class WhoToFollowPanel extends ImmutablePureComponent {
'suggestions',
]
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.isHidden && nextProps.isIntersecting && !prevState.fetched) {
return {
fetched: true
}
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.fetched && this.state.fetched) {
this.props.fetchSuggestions()
}
componentDidMount() {
this.props.fetchSuggestions()
}
render() {

@ -5,6 +5,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import classNames from 'classnames/bind'
import escapeTextContentForBrowser from 'escape-html'
import spring from 'react-motion/lib/spring'
import { me } from '../initial_state'
import Motion from '../features/ui/util/optional_motion'
import { vote } from '../actions/polls'
import emojify from './emoji/emoji'
@ -202,7 +203,7 @@ class Poll extends ImmutablePureComponent {
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
{
!showResults &&
!showResults && me &&
<Button
isNarrow
className={_s.mr10}

@ -65,7 +65,7 @@ class StatusSharePopover extends ImmutablePureComponent {
render() {
const { intl, status } = this.props
const mailToHref = !status ? undefined : `mailto:?subject=&body=${status.get('url')}`
const mailToHref = !status ? undefined : `mailto:?subject=Gab&body=${status.get('url')}`
return (
<PopoverLayout width={220}>

@ -173,7 +173,13 @@ class ProfileHeader extends ImmutablePureComponent {
</div>
<div className={[_s.default, _s.flexRow, _s.flexNormal, _s.py10].join(' ')}>
<DisplayName account={account} isMultiline noRelationship isLarge noHover />
<DisplayName
account={account}
isMultiline
isLarge
isCentered
noHover
/>
</div>
</div>
@ -214,7 +220,7 @@ class ProfileHeader extends ImmutablePureComponent {
<input type='hidden' value={account.get('username')} name='username' />
</form>
<div className={[_s.default, _s.flexRow, _s.pb3, _s.mr10].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.mr10].join(' ')}>
<AccountActionButton account={account} />
</div>
@ -278,7 +284,7 @@ class ProfileHeader extends ImmutablePureComponent {
</div>
<div className={[_s.default, _s.flexRow, _s.px15, _s.flexNormal, _s.py10].join(' ')}>
<DisplayName account={account} isMultiline noRelationship isLarge noHover />
<DisplayName account={account} isMultiline isLarge noHover />
</div>
</div>

@ -18,8 +18,8 @@ export default class ProfileNavigationBar extends PureComponent {
<div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}>
<BackButton
classNames={[_s.height53PX, _s.bgTransparent, _s.ml10, _s.mr10, _s.cursorPointer, _s.outlineNone, _s.default, _s.justifyContentCenter].join(' ')}
iconSize='32px'
className={[_s.height53PX, _s.pl10, _s.pr10].join(' ')}
iconSize='18px'
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
/>

@ -63,10 +63,6 @@ export default
@injectIntl
class Sidebar extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
static propTypes = {
intl: PropTypes.object.isRequired,
account: ImmutablePropTypes.map,
@ -94,18 +90,6 @@ class Sidebar extends ImmutablePureComponent {
})
}
historyBack = () => {
if (window.history && window.history.length === 1) {
this.context.router.history.push('/home')
} else {
this.context.router.history.goBack()
}
}
handleBackClick = () => {
this.historyBack()
}
setMoreButtonRef = n => {
this.moreBtnRef = n
}
@ -221,14 +205,16 @@ class Sidebar extends ImmutablePureComponent {
<header role='banner' className={[_s.default, _s.flexGrow1, _s.z3, _s.alignItemsEnd].join(' ')}>
<div className={[_s.default, _s.width240PX].join(' ')}>
<div className={[_s.default, _s.posFixed, _s.heightCalc53PX, _s.bottom0].join(' ')}>
<div className={[_s.default, _s.height100PC, _s.alignItemsStart, _s.width240PX, _s.pr15, _s.py10, _s.overflowYScroll].join(' ')}>
<div className={[_s.default, _s.height100PC, _s.alignItemsStart, _s.width240PX, _s.pr15, _s.py10, _s.noScrollbar, _s.overflowYScroll].join(' ')}>
<div className={[_s.default, _s.width100PC].join(' ')}>
{
!!title &&
<div className={[_s.default, _s.flexRow, _s.px5, _s.pt10].join(' ')}>
{
showBackBtn &&
<BackButton />
<BackButton
icon='arrow-left'
/>
}
<Heading size='h1'>
{title}

@ -39,8 +39,6 @@ class StatusPrepend extends ImmutablePureComponent {
const isRepost = (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object')
console.log("isComment:", isComment)
if (!isFeatured && !isPromoted && !isRepost && !isComment) return null
let iconId

@ -117,7 +117,7 @@ class Notifications extends ImmutablePureComponent {
// : todo : include follow requests
console.log('--0--notifications:', notifications)
console.log('--0--notifications:', hasMore)
if (isLoading && this.scrollableContent) {
scrollableContent = this.scrollableContent

@ -370,6 +370,7 @@ class UI extends PureComponent {
}
this.props.dispatch(expandHomeTimeline())
this.props.dispatch(expandNotifications())
this.props.dispatch(initializeNotifications())
setTimeout(() => {

@ -21,6 +21,7 @@ export default class ProfileLayout extends ImmutablePureComponent {
account: ImmutablePropTypes.map,
children: PropTypes.node.isRequired,
title: PropTypes.string,
unavailable: PropTypes.bool,
}
render() {
@ -28,6 +29,7 @@ export default class ProfileLayout extends ImmutablePureComponent {
account,
children,
title,
unavailable,
} = this.props
return (
@ -101,7 +103,7 @@ export default class ProfileLayout extends ImmutablePureComponent {
<div className={[_s.default, _s.width340PX].join(' ')}>
<ProfileStatsPanel account={account} />
<ProfileInfoPanel account={account} />
<MediaGalleryPanel account={account} />
{ !unavailable && <MediaGalleryPanel account={account} /> }
<LinkFooter />
</div>
</Sticky>

@ -37,7 +37,6 @@ class GroupPage extends ImmutablePureComponent {
}
componentDidMount() {
console.log("group page mounted:", this.props.params.id)
this.props.onFetchGroup(this.props.params.id)
}
@ -54,16 +53,9 @@ class GroupPage extends ImmutablePureComponent {
return (
<GroupLayout
showBackBtn
title={intl.formatMessage(messages.group)}
title={groupTitle}
group={group}
relationships={relationships}
actions={[
// : todo :
// {
// icon: 'ellipsis',
// onClick: null,
// },
]}
>
<PageTitle path={[groupTitle, intl.formatMessage(messages.group)]} />

@ -16,6 +16,7 @@ const filters = [
'reblog',
'poll',
'follow',
'follow_requests',
]
const messages = defineMessages({
@ -25,6 +26,7 @@ const messages = defineMessages({
reblog: { id: 'reposts', defaultMessage: 'Reposts' },
poll: { id: 'polls', defaultMessage: 'Poll' },
follow: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
all: { id: 'notifications.filter.all', defaultMessage: 'All' },
})
@ -35,7 +37,6 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = (dispatch) => ({
setFilter(value) {
console.log("SETTING ACTIVE FILTER:", value)
dispatch(setFilter('active', value))
},
})

@ -1,4 +1,4 @@
import { Fragment } from 'react'
import { FormattedMessage } from 'react-intl'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { fetchAccountByUsername } from '../actions/accounts'
@ -6,6 +6,7 @@ import { makeGetAccount } from '../selectors'
import { me } from '../initial_state'
import PageTitle from '../features/ui/util/page_title'
import ColumnIndicator from '../components/column_indicator'
import Block from '../components/block'
import ProfileLayout from '../layouts/profile_layout'
const mapStateToProps = (state, { params: { username } }) => {
@ -63,6 +64,7 @@ class ProfilePage extends ImmutablePureComponent {
<ProfileLayout
account={account}
title={name}
unavailable={unavailable}
>
<PageTitle path={`${name} (@${username})`} />
{
@ -74,6 +76,12 @@ class ProfilePage extends ImmutablePureComponent {
account,
})
}
{
unavailable &&
<ColumnIndicator type='error' message={
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
} />
}
</ProfileLayout>
)
}

@ -12,7 +12,7 @@ export const shortNumberFormat = (number) => {
return (
<Fragment>
<FormattedNumber value={number / 1000} maximumFractionDigits={1} />
<FormattedNumber value={number / 1000} maximumFractionDigits={1} />k
</Fragment>
)
}

@ -1,4 +1,5 @@
:root {
--color_highlight: #CCF3DF;
--color_brand-dark: #378e61;
--color_brand-light: #63DA9D;
--color_brand-light-opaque: rgb(54, 233, 145, .125);
@ -94,6 +95,14 @@
--navigation_brand: var(--color_brand) !important;
}
::selection {
background: var(--color_highlight);
}
::-moz-selection {
background: var(--color_highlight);
}
html,
body {
height: 100%;
@ -296,6 +305,7 @@ body {
.borderBottom6PX { border-bottom-width: 6px; }
.borderLeft1PX { border-left-width: 1px; }
.borderTop1PX { border-top-width: 1px; }
.borderTop2PX { border-top-width: 2px; }
.border1PX { border-width: 1px; }
.border2PX { border-width: 2px; }