Preact minor updates batch 15

Minor
- Fix `/help` for server commands
- Fix multiline DMs
- Redesign collapsed userlists
- Refactor topbar to use display:table and inline-blocks, instead of
  floats and manual margin calculations
  - I nearly used flexbox, but fake tables work just as well so I
    figure why not. The old client actually manually calculates margin
    to do the thing where the right tabbar expands to the right and
    then to the left and it's nice to not to need that anymore.
- Document `.gray` in STYLING
- Convert all buttons that make sense as links to <a class="button">
  - That would be all the main menu links, plus the
    usercount/battlecount in Rooms.
- Fix dismissing popups in iOS Safari
- Add an icon for being disconnected

Trivial
- Fix stars in the old client format dropdown
- Use "Cmd" instead of "Ctrl" on Macs in the chat formatting panel
- Fix some code style in panel-popups
- Update supported browser list in README
- Add Preact keys where they make sense
  - For the most part I think requiring keys is too strict. I still
    think they're unnecessary or actively detrimental for any list
    that isn't long/complex and also rearranged ever.
- ESLint: Set no-shadow to warn
This commit is contained in:
Guangcong Luo 2025-04-22 01:11:57 +00:00
parent 90917c4d4f
commit bee9e8e629
13 changed files with 235 additions and 198 deletions

View File

@ -25,16 +25,17 @@ Browser support
Pokémon Showdown currently supports, in order of preference:
- Chrome
- Chromium browsers (Edge, Vivaldi, Brave, Opera...)
- Firefox
- Opera
- Safari 5+
- IE11+
- IE11+ and Edge
- Chrome/Firefox/Safari for various mobile devices
- any remotely modern browser
Pokémon Showdown is usable, but expect degraded performance and certain features not to work in:
Pokémon Showdown is usable, but expect degraded performance and certain features not to work in extremely legacy browsers like:
- Safari 4+
- IE9+
- Safari 4
- IE9-10
Pokémon Showdown is mostly developed on Chrome, and Chrome or the desktop client is required for certain features like dragging-and-dropping teams from PS to your computer. However, bugs reported on any supported browser will usually be fixed pretty quickly.

View File

@ -100,7 +100,7 @@ export const defaultRules = {
"no-return-assign": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow": "error",
"no-shadow": "warn",
"no-template-curly-in-string": "error",
"no-throw-literal": "warn",
"no-unmodified-loop-condition": "error",

View File

@ -1227,6 +1227,8 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
this.add('||/afd off - Disable April Fools\' Day jokes until the next refresh, and set /afd default.');
this.add('||/afd never - Disable April Fools\' Day jokes permanently.');
return false;
default:
return true;
}
},
'autojoin,cmd,crq,query'() {

View File

@ -70,7 +70,7 @@ class BattlesPanel extends PSRoomPanel<BattlesRoom> {
renderBattleLink(battle: BattleDesc) {
const format = battle.id.split('-')[1];
const minEloMessage = typeof battle.minElo === 'number' ? `rated ${battle.minElo}` : battle.minElo;
return <div><a href={`/${battle.id}`} class="blocklink">
return <div key={battle.id}><a href={`/${battle.id}`} class="blocklink">
{minEloMessage && <small style="float:right">({minEloMessage})</small>}
<small>[{format}]</small><br />
<em class="p1">{battle.p1}</em> <small class="vs">vs.</small> <em class="p2">{battle.p2}</em>

View File

@ -489,7 +489,8 @@ export class ChatRoom extends PSRoom {
}
override sendDirect(line: string) {
if (this.pmTarget) {
PS.send(`|/pm ${this.pmTarget}, ${line}`);
line = line.split('\n').filter(Boolean).map(row => `/pm ${this.pmTarget!}, ${row}`).join('\n');
PS.send(`|${line}`);
return;
}
super.sendDirect(line);
@ -1057,49 +1058,59 @@ class ChatPanel extends PSRoomPanel<ChatRoom> {
}
export class ChatUserList extends preact.Component<{
room: ChatRoom, left?: number, top?: number, minimized?: boolean,
room: ChatRoom, left?: number, top?: number, minimized?: boolean, static?: boolean,
}> {
override state = {
expanded: false,
};
toggleExpanded = () => {
this.setState({ expanded: !this.state.expanded });
};
render() {
const room = this.props.room;
let userList = Object.entries(room.users) as [ID, string][];
PSUtils.sortBy(userList, ([id, name]) => (
[PS.server.getGroup(name.charAt(0)).order, !name.endsWith('@!'), id]
));
return <ul
class={'userlist' + (this.props.minimized ? (this.state.expanded ? ' userlist-maximized' : ' userlist-minimized') : '')}
const pmTargetid = room.pmTarget ? toID(room.pmTarget) : null;
return <div
class={'userlist' + (this.props.minimized ? ' userlist-hidden' : this.props.static ? ' userlist-static' : '')}
style={{ left: this.props.left || 0, top: this.props.top || 0 }}
>
<li class="userlist-count" onClick={this.toggleExpanded}><small>{room.userCount} users</small></li>
{userList.map(([userid, name]) => {
const groupSymbol = name.charAt(0);
const group = PS.server.groups[groupSymbol] || { type: 'user', order: 0 };
let color;
if (name.endsWith('@!')) {
name = name.slice(0, -2);
color = '#888888';
} else {
color = BattleLog.usernameColor(userid);
}
return <li key={userid}><button class="userbutton username">
<em class={`group${['leadership', 'staff'].includes(group.type!) ? ' staffgroup' : ''}`}>
{groupSymbol}
</em>
{group.type === 'leadership' ? (
<strong><em style={{ color }}>{name.slice(1)}</em></strong>
) : group.type === 'staff' ? (
<strong style={{ color }}>{name.slice(1)}</strong>
) : (
<span style={{ color }}>{name.slice(1)}</span>
)}
</button></li>;
})}
</ul>;
{!this.props.minimized ? (
<div class="userlist-count"><small>{room.userCount} users</small></div>
) : room.id === 'dm-' ? (
<>
<button class="button button-middle" data-cmd="/help">Commands</button>
</>
) : pmTargetid ? (
<>
<button class="button button-middle" data-cmd="/challenge">Challenge</button>
<button class="button button-middle" data-href={`useroptions-${pmTargetid}`}>{'\u2026'}</button>
</>
) : (
<button data-href="userlist" class="button button-middle">{room.userCount} users</button>
)}
<ul>
{userList.map(([userid, name]) => {
const groupSymbol = name.charAt(0);
const group = PS.server.groups[groupSymbol] || { type: 'user', order: 0 };
let color;
if (name.endsWith('@!')) {
name = name.slice(0, -2);
color = '#888888';
} else {
color = BattleLog.usernameColor(userid);
}
return <li key={userid}><button class="userbutton username">
<em class={`group${['leadership', 'staff'].includes(group.type!) ? ' staffgroup' : ''}`}>
{groupSymbol}
</em>
{group.type === 'leadership' ? (
<strong><em style={{ color }}>{name.slice(1)}</em></strong>
) : group.type === 'staff' ? (
<strong style={{ color }}>{name.slice(1)}</strong>
) : (
<span style={{ color }}>{name.slice(1)}</span>
)}
</button></li>;
})}
</ul>
</div>;
}
}

View File

@ -546,7 +546,10 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
if (!PS.user.userid || PS.isOffline) {
return <TeamForm class="menugroup" onSubmit={this.submitSearch}>
<button class="mainmenu1 mainmenu big button disabled" disabled name="search">
<em>{PS.isOffline ? "Disconnected" : "Connecting..."}</em>
<em>{PS.isOffline ? [<span class="fa-stack fa-lg">
<i class="fa fa-plug fa-flip-horizontal fa-stack-1x"></i>
<i class="fa fa-ban fa-stack-2x text-danger"></i>
</span>, " Disconnected"] : "Connecting..."}</em>
</button>
{PS.isOffline && <p class="buttonbar">
<button class="button" data-cmd="/reconnect"><i class="fa fa-plug"></i> <strong>Reconnect</strong></button>
@ -591,22 +594,22 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
{this.renderSearchButton()}
<div class="menugroup">
<p><button class="mainmenu2 mainmenu button" data-href="teambuilder">Teambuilder</button></p>
<p><button class={"mainmenu3 mainmenu" + onlineButton} data-href="ladder">Ladder</button></p>
<p><button class={"mainmenu4 mainmenu" + onlineButton} data-href="view-tournaments-all">Tournaments</button></p>
<p><a class="mainmenu2 mainmenu button" href="teambuilder">Teambuilder</a></p>
<p><a class={"mainmenu3 mainmenu" + onlineButton} href="ladder">Ladder</a></p>
<p><a class={"mainmenu4 mainmenu" + onlineButton} href="view-tournaments-all">Tournaments</a></p>
</div>
<div class="menugroup">
<p><button class={"mainmenu4 mainmenu" + onlineButton} data-href="battles">Watch a battle</button></p>
<p><button class={"mainmenu5 mainmenu" + onlineButton} data-href="users">Find a user</button></p>
<p><button class={"mainmenu6 mainmenu" + onlineButton} data-href="view-friends-all">Friends</button></p>
<p><a class={"mainmenu4 mainmenu" + onlineButton} href="battles">Watch a battle</a></p>
<p><a class={"mainmenu5 mainmenu" + onlineButton} href="users">Find a user</a></p>
<p><a class={"mainmenu6 mainmenu" + onlineButton} href="view-friends-all">Friends</a></p>
</div>
</div>
<div class="mainmenu-right" style={{ display: PS.leftPanelWidth ? 'none' : 'block' }}>
<div class="menugroup">
<p><button class={"mainmenu1 mainmenu" + onlineButton} data-href="rooms">Chat rooms</button></p>
<p><a class={"mainmenu1 mainmenu" + onlineButton} href="rooms">Chat rooms</a></p>
{PS.server.id !== 'showdown' && (
<p><button class={"mainmenu2 mainmenu" + onlineButton} data-href="lobby">Lobby chat</button></p>
<p><a class={"mainmenu2 mainmenu" + onlineButton} href="lobby">Lobby chat</a></p>
)}
</div>
</div>

View File

@ -6,8 +6,8 @@ import { PSLoginServer } from "./client-connection";
import { PSBackground } from "./client-core";
import { PSRoom, type RoomOptions, PS, type PSLoginState, type RoomID, type TimestampOptions } from "./client-main";
import { type BattleRoom } from "./panel-battle";
import type { ChatRoom } from "./panel-chat";
import { PSRoomPanel, PSPanelWrapper } from "./panels";
import { ChatUserList, type ChatRoom } from "./panel-chat";
import { PSRoomPanel, PSPanelWrapper, PSView } from "./panels";
import { PSHeader } from "./panel-topbar";
/**
@ -412,6 +412,24 @@ class UserOptionsPanel extends PSRoomPanel {
}
}
class UserListPanel extends PSRoomPanel {
static readonly id = 'userlist';
static readonly routes = ['userlist'];
static readonly location = 'semimodal-popup';
static readonly noURL = true;
override render() {
const room = this.props.room;
const parentRoom = room.getParent() as ChatRoom;
if (parentRoom.type !== 'chat' && parentRoom.type !== 'battle') {
throw new Error(`UserListPanel: ${room.id} is not a chat room`);
}
return <PSPanelWrapper room={room} width={280}><div class="pad">
<ChatUserList room={parentRoom} static />
</div></PSPanelWrapper>;
}
}
class VolumePanel extends PSRoomPanel {
static readonly id = 'volume';
static readonly routes = ['volume'];
@ -1181,12 +1199,12 @@ class RegisterPanel extends PSRoomPanel {
<p>
<label class="label"><img
src="https://play.pokemonshowdown.com/sprites/gen5ani/pikachu.gif"
alt="An Electric-type mouse that is the mascot of the Pokémon franchise."
alt="An Electric-type mouse that is the mascot of the Pok&eacute;mon franchise."
/></label>
</p>
<p>
<label class="label">
What is this pokemon?{}
What is this pokemon? {}
<input name="captcha" class="textbox" />
</label>
</p>
@ -1262,27 +1280,28 @@ class BackgroundListPanel extends PSRoomPanel {
<p><strong>Official</strong></p>
<div class="bglist">
<button onClick={this.setBg} value="charizards" class={option('charizards')}>
<span class="bg" style="background-position: 0 -0px"></span>{' '}
<span class="bg" style="background-position: 0 -0px"></span>{}
Charizards
</button>
<button onClick={this.setBg} value="horizon" class={option('horizon')}>
<span class="bg" style="background-position: 0 -90px"></span>{' '}
<span class="bg" style="background-position: 0 -90px"></span>{}
Horizon
</button>
<button onClick={this.setBg} value="waterfall" class={option('waterfall')}>
<span class="bg" style="background-position: 0 -180px"></span>{' '}
<span class="bg" style="background-position: 0 -180px"></span>{}
Waterfall
</button>
<button onClick={this.setBg} value="ocean" class={option('ocean')}>
<span class="bg" style="background-position: 0 -270px"></span>{' '}
<span class="bg" style="background-position: 0 -270px"></span>{}
Ocean
</button>
<button onClick={this.setBg} value="shaymin" class={option('shaymin')}>
<span class="bg" style="background-position: 0 -360px"></span>{' '}
<span class="bg" style="background-position: 0 -360px"></span>{}
Shaymin
</button>
<button onClick={this.setBg} value="solidblue" class={option('solidblue')}>
<span class="bg" style="background: #344b6c"></span>Solid blue
<span class="bg" style="background: #344b6c"></span>{}
Solid blue
</button>
</div>
<div style="clear: left"></div>
@ -1318,11 +1337,12 @@ class ChatFormattingPanel extends PSRoomPanel {
override render() {
const room = this.props.room;
const ctrl = PSView.isMac ? 'Cmd' : 'Ctrl';
return <PSPanelWrapper room={room} width={480}><div class="pad">
<p>Usable formatting:</p>
<p>**<strong>bold</strong>** (<kbd>Ctrl</kbd> + <kbd>B</kbd>)</p>
<p>__<em>italics</em>__ (<kbd>Ctrl</kbd> + <kbd>I</kbd>)</p>
<p>``<code>code formatting</code>`` (<kbd>Ctrl</kbd> + <kbd>`</kbd>)</p>
<p>**<strong>bold</strong>** (<kbd>{ctrl}</kbd>+<kbd>B</kbd>)</p>
<p>__<em>italics</em>__ (<kbd>{ctrl}</kbd>+<kbd>I</kbd>)</p>
<p>``<code>code formatting</code>`` (<kbd>Ctrl</kbd>+<kbd>`</kbd>)</p>
<p>~~<s>strikethrough</s>~~</p>
<p>^^<sup>superscript</sup>^^</p>
<p>\\<sub>subscript</sub>\\</p>
@ -1333,8 +1353,7 @@ class ChatFormattingPanel extends PSRoomPanel {
type="checkbox"
name="greentext"
checked={PS.prefs.chatformatting.hidegreentext}
/> Suppress{' '}
<span class="greentext">&gt;greentext</span>
/> Suppress <span class="greentext">&gt;greentext</span>
</label>
</p>
<p>
@ -1355,8 +1374,7 @@ class ChatFormattingPanel extends PSRoomPanel {
type="checkbox"
name="spoiler"
checked={PS.prefs.chatformatting.hidespoiler}
/> Auto-show spoilers:{' '}
<span class="spoiler">these things</span>
/> Auto-show spoilers: <span class="spoiler">these things</span>
</label>
</p>
<p>
@ -1366,8 +1384,7 @@ class ChatFormattingPanel extends PSRoomPanel {
type="checkbox"
name="links"
checked={PS.prefs.chatformatting.hidelinks}
/> Make [[clickable links]]
unclickable
/> Make [[clickable links]] unclickable
</label>
</p>
<p>
@ -1499,6 +1516,7 @@ class RoomTabListPanel extends PSRoomPanel {
PS.addRoomType(
UserPanel,
UserOptionsPanel,
UserListPanel,
VolumePanel,
OptionsPanel,
LoginPanel,

View File

@ -157,20 +157,20 @@ class RoomsPanel extends PSRoomPanel {
<i class="fa fa-caret-right"></i> Hide
</button>
<div class="roomcounters">
<button class="button" data-href="/users" title="Find an online user">
<a class="button" href="users" title="Find an online user">
<span
class="pixelated usercount"
title="Meloetta is PS's mascot! The Aria forme is about using its voice, and represents our chatrooms."
></span>
<strong>{rooms.userCount || '-'}</strong> users online
</button> {}
<button class="button" data-href="/battles" title="Watch an active battle">
</a> {}
<a class="button" href="battles" title="Watch an active battle">
<span
class="pixelated battlecount"
title="Meloetta is PS's mascot! The Pirouette forme is Fighting-type, and represents our battles."
></span>
<strong>{rooms.battleCount || '-'}</strong> active battles
</button>
</a>
</div>
<div>
<input
@ -188,7 +188,7 @@ class RoomsPanel extends PSRoomPanel {
const sortedRooms = rooms.sort((a, b) => (b.userCount || 0) - (a.userCount || 0));
return <div class="roomlist">
<h2>{title}</h2>
{sortedRooms.map(roomInfo => <div>
{sortedRooms.map(roomInfo => <div key={roomInfo.title}>
<a href={`/${toID(roomInfo.title)}`} class="blocklink">
{roomInfo.userCount !== undefined && <small style="float:right">({roomInfo.userCount} users)</small>}
<strong><i class="fa fa-comment-o"></i> {roomInfo.title}<br /></strong>

View File

@ -152,40 +152,27 @@ export class PSHeader extends preact.Component<{ style: object }> {
</button>;
}
const ariaLabel = id === 'rooms' ? { "aria-label": "Join chat" } : {};
return <li>
return <li class={id === '' ? 'home-li' : ''}>
<a
class={className} href={`/${id}`} draggable={true} title={hoverTitle || undefined}
onDragEnter={this.handleDragEnter} onDragStart={this.handleDragStart}
{...ariaLabel}
>
{icon} <span>{roomTitle}</span>
{icon} {roomTitle}
</a>
{closeButton}
</li>;
}
sideRoomMargin = 0;
handleRoomTabOverflow = () => {
if (PS.leftPanelWidth === null || !this.base) return;
const userbarLeft = this.base.querySelector('div.userbar')?.getBoundingClientRect()?.left;
const plusTab = this.base.querySelector('a.roomtab[aria-label="Join chat"]');
const plusTabRight = plusTab?.getBoundingClientRect()?.right;
const plusTabRight = this.base.querySelector('a.roomtab[aria-label="Join chat"]')?.getBoundingClientRect()?.right;
const overflow = this.base.querySelector<HTMLElement>('.overflow');
const siderooms = this.base.querySelector<HTMLElement>('.siderooms');
if (!overflow || !userbarLeft || !plusTab || !plusTabRight || !siderooms) return;
if (!overflow || !userbarLeft || !plusTabRight) return;
// this has too much perf impact and bugs... just use vertical tabs
// if (PS.leftPanelWidth && Math.abs((userbarLeft - 5) - plusTabRight) > 0.5) {
// this.sideRoomMargin += (userbarLeft - 5) - plusTabRight;
// if (this.sideRoomMargin < 0) this.sideRoomMargin = 0;
// this.sideRoomMargin = Math.min(Math.max(PS.leftPanelWidth - 52, 0), this.sideRoomMargin);
// siderooms.style.marginLeft = `${this.sideRoomMargin}px`;
// plusTabRight = plusTab.getBoundingClientRect().right;
// }
if (plusTabRight > userbarLeft) {
if (plusTabRight > userbarLeft - 3) {
overflow.style.display = 'block';
} else {
overflow.style.display = 'none';
@ -260,32 +247,31 @@ export class PSHeader extends preact.Component<{ style: object }> {
} else {
document.documentElement.classList?.remove('scroll-snap-enabled');
}
this.sideRoomMargin = Math.max(PS.leftPanelWidth - 52, 0);
return <div id="header" class="header" style={this.props.style}>
<div class="maintabbarbottom"></div>
<img
class="logo"
src={`https://${Config.routes.client}/favicon-256.png`}
alt="Pokémon Showdown! (beta)"
width="50" height="50"
/>
<div class="tabbar maintabbar"><div class="inner">
<ul>
<div class="tabbar maintabbar"><div class="inner-1"><div class="inner-2">
<ul class="maintabbar-left" style={{ width: `${PS.leftPanelWidth}px` }}>
<li>
<img
class="logo"
src={`https://${Config.routes.client}/favicon-256.png`}
alt="Pokémon Showdown! (beta)"
width="48" height="48"
/>
</li>
{PSHeader.renderRoomTab(PS.leftRoomList[0])}
</ul>
<ul>
{PS.leftRoomList.slice(1).map(roomid => PSHeader.renderRoomTab(roomid))}
</ul>
<ul class="siderooms" style={{ float: 'none', marginLeft: this.sideRoomMargin }}>
<ul class="maintabbar-right">
{PS.rightRoomList.map(roomid => PSHeader.renderRoomTab(roomid))}
</ul>
<div class="overflow">
<button name="tablist" class="button" data-href="roomtablist" aria-label="All tabs" type="button">
<i class="fa fa-caret-down"></i>
</button>
</div>
</div></div>
</div></div></div>
<div class="overflow">
<button name="tablist" class="button" data-href="roomtablist" aria-label="All tabs" type="button">
<i class="fa fa-caret-down"></i>
</button>
</div>
<div class="userbar">
{this.renderUser()} {}
<button class="icon button" data-href="volume" title="Sound" aria-label="Sound" onDblClick={PSHeader.toggleMute}>

View File

@ -369,12 +369,6 @@ export class PSView extends preact.Component {
window.addEventListener('click', ev => {
let elem = ev.target as HTMLElement | null;
if (elem?.className === 'ps-overlay') {
PS.closePopup();
ev.preventDefault();
ev.stopImmediatePropagation();
return;
}
const clickedRoom = PS.getRoom(elem);
while (elem) {
if (elem.className === 'spoiler') {
@ -587,6 +581,15 @@ export class PSView extends preact.Component {
room.autoDismissNotifications();
PS.setFocus(room);
};
handleClickOverlay = (ev: MouseEvent) => {
// iOS Safari bug, no global click events when tapping
// I'm sure it's intentional but it interferes with putting the dismiss feature in window.onclick
if ((ev.target as Element)?.className === 'ps-overlay') {
PS.closePopup();
ev.preventDefault();
ev.stopImmediatePropagation();
}
};
handleButtonClick(elem: HTMLButtonElement) {
switch (elem.name) {
case 'closeRoom': {
@ -770,7 +773,7 @@ export class PSView extends preact.Component {
if (room.location === 'popup' && room.parentElem) {
return <Panel key={room.id} room={room} />;
}
return <div key={room.id} class="ps-overlay">
return <div key={room.id} class="ps-overlay" onClick={this.handleClickOverlay}>
<Panel room={room} />
</div>;
}

View File

@ -445,7 +445,7 @@ h1, h2, p {
</div>
<div class="clear"></div>
<p><code>.broadcast-blue, .broadcast-green, .broadcast-red, .infobox</code></p>
<p><code>.broadcast-blue, .broadcast-green, .broadcast-red</code></p>
<p>
Remember that broadcasts can have links inside them, which should be readable.
@ -455,19 +455,33 @@ h1, h2, p {
<div class="broadcast-blue"><strong>Important:</strong> Something <a href="./">happened</a>!</div>
<div class="broadcast-green"><strong>Yay:</strong> Something <a href="./">succeeded</a>!</div>
<div class="broadcast-red"><strong>Error:</strong> Something <a href="./">failed</a>!</div>
<br /><div class="infobox">I'm sure you see these everywhere.</div>
</div>
<div class="dark-container dark">
<div class="broadcast-blue"><strong>Important:</strong> Something <a href="./">happened</a>!</div>
<div class="broadcast-green"><strong>Yay:</strong> Something <a href="./">succeeded</a>!</div>
<div class="broadcast-red"><strong>Error:</strong> Something <a href="./">failed</a>!</div>
<br /><div class="infobox">I'm sure you see these everywhere.</div>
</div>
<div class="dark-container code dark">
&lt;div class="broadcast-blue">&lt;strong>Important:&lt;/strong> Something &lt;a href="./">happened&lt;/a>!&lt;/div>
&lt;div class="broadcast-green">&lt;strong>Yay:&lt;/strong> Something &lt;a href="./">succeeded&lt;/a>!&lt;/div>
&lt;div class="broadcast-red">&lt;strong>Error:&lt;/strong> Something &lt;a href="./">failed&lt;/a>!&lt;/div>
&lt;br />&lt;div class="infobox">I'm sure you see these everywhere.&lt;/div>
</div>
<div class="clear"></div>
<p><code>.infobox, .gray</code></p>
<p>
Infoboxes are used for organization. Gray makes things gray.
</p>
<div class="light-container">
<div class="infobox">These are <span class="gray">used all over</span> Showdown.</div>
</div>
<div class="dark-container dark">
<div class="infobox">These are <span class="gray">used all over</span> Showdown.</div>
</div>
<div class="dark-container code dark">
&lt;div class="infobox">These are &lt;span class="gray">used all over&lt;/span> Showdown.&lt;/div>
</div>
<div class="clear"></div>

View File

@ -57,6 +57,8 @@ summary:hover span.ilink {
background: transparent;
padding: 0;
margin: 0;
}
button.subtle {
font: inherit;
}
.subtle:hover {

View File

@ -96,7 +96,7 @@ pre {
padding: 0;
}
.header .logo {
float: left;
display: block;
margin: 0;
}
.header-vertical {
@ -116,7 +116,6 @@ pre {
position: absolute;
top: 12px;
right: 12px;
font-weight: bold;
}
.header-vertical .userbar {
right: auto;
@ -127,6 +126,7 @@ pre {
word-wrap: break-word;
}
.userbar .username {
font-weight: bold;
color: black;
text-shadow: 1px 1px 0 #f8f8f8, 1px -1px 0 #f8f8f8, -1px 1px 0 #f8f8f8, -1px -1px 0 #f8f8f8;
}
@ -156,25 +156,6 @@ pre {
margin: 0 -5px;
}
.tabbar {
position: absolute;
bottom: 1px;
left: 0;
right: 0;
display: block;
list-style: none;
margin: 0;
padding: 2px 0 0 0;
height: 37px;
text-align: left;
color: white;
text-shadow: 0 1px 0 black;
font-size: 10pt;
line-height: 100%;
}
.mini-header, .maintabbarbottom {
background: #f8f8f8;
border: solid 1px #AAAAAA;
@ -186,7 +167,7 @@ pre {
position: absolute;
left: 0;
right: 0;
bottom: -6px;
top: 49px;
height: 6px;
border-left-width: 0;
border-right-width: 0;
@ -285,24 +266,42 @@ pre {
border-bottom-color: #5A5A5A;
}
.tabbar {
display: block;
list-style: none;
margin: 0;
padding: 2px 0 0 0;
text-align: left;
color: white;
text-shadow: 0 1px 0 black;
font-size: 10pt;
line-height: 100%;
}
.tabbar.maintabbar {
margin-left: 52px;
/* margin-left: 52px; */
padding: 0;
margin-right: 180px;
overflow: hidden;
white-space: nowrap;
}
.tabbar.maintabbar.minitabbar {
margin-left: 0;
.maintabbar .inner-1 {
display: table; /* ;) */
width: 100%;
height: 49px;
line-height: 49px;
}
.maintabbar .inner {
width: 400%; /* make sure overflows go right and not down */
.maintabbar .inner-2 {
display: table-row;
}
.maintabbar .overflow {
.header .overflow {
position: absolute;
bottom: 0;
right: 0;
right: 180px;
z-index: 12;
}
.maintabbar .overflow .button {
.header .overflow .button {
height: 37px;
width: 32px;
font-size: 12pt;
@ -315,17 +314,29 @@ pre {
content: "\f0e6";
}
.tabbar li,
.tabbar ul {
display: block;
float: left;
list-style: none;
margin: 0;
padding: 0;
}
.tabbar a.button {
.tabbar .maintabbar-left, .tabbar .maintabbar-right {
display: table-cell;
list-style: none;
margin: 0;
padding: 0;
}
.tabbar .maintabbar-right {
padding-right: 1px;
}
.tabbar li {
display: inline-block;
vertical-align: bottom;
line-height: normal;
}
.tabbar a.button {
display: inline-block; /* to align it with the close button */
height: 28px;
min-width: 52px;
white-space: nowrap;
@ -333,14 +344,10 @@ pre {
position: relative;
padding: 3px 2px 3px 2px;
margin: 0 -1px 0 0;
top: 1px;
border-radius: 0;
box-shadow: inset 0 -1px 2px rgba(255,255,255,1);
font-size: 11px;
}
.tabbar a.button.cur {
box-shadow: none;
}
.tabbar a.button i {
display: block;
text-align: center;
@ -352,10 +359,6 @@ pre {
.tabbar a.button i.rooms-plus {
margin: 7px auto -6px auto;
}
.tabbar a.button span {
display: block;
overflow: hidden;
}
.tabbar a.button i.text {
font-size: 11px;
margin-right: 8px;
@ -374,16 +377,16 @@ pre {
}
.tabbar a.button.cur,
.tabbar a.button.cur:hover {
box-shadow: none;
border-bottom: 0;
padding-top: 3px;
padding-bottom: 5px;
top: 0px;
}
.tabbar li:first-child a.button {
.tabbar li:first-child a.button, .tabbar li.home-li a.button, .tabbar li.home-li + li a.button {
border-top-left-radius: 5px;
margin-left: 6px;
}
.tabbar li:last-child a.button {
.tabbar li:last-child a.button, .tabbar li.home-li a.button {
border-top-right-radius: 5px;
}
.tabbar a.closable {
@ -1016,17 +1019,17 @@ form.menugroup {
margin: 38px 0 15px 0;
}
}
.roomcounters button {
.roomcounters .button {
margin: 0 auto;
width: 120px;
height: 60px;
padding: 3px 8px;
width: 118px;
height: 52px;
padding: 6px 0 0 0;
text-align: center;
vertical-align: middle;
font-size: 10pt;
position: relative;
}
.roomcounters button strong {
.roomcounters .button strong {
display: block;
font-size: 18pt;
font-weight: normal;
@ -1040,7 +1043,7 @@ form.menugroup {
bottom: -5px;
background: url(https://play.pokemonshowdown.com/sprites/gen5/meloetta.png) center no-repeat;
}
.roomcounters button:hover .usercount {
.roomcounters .button:hover .usercount {
background: url(https://play.pokemonshowdown.com/sprites/gen5ani/meloetta.gif) center no-repeat;
}
.roomcounters .battlecount {
@ -1052,7 +1055,7 @@ form.menugroup {
bottom: -10px;
background: url(https://play.pokemonshowdown.com/sprites/gen5/meloetta-pirouette.png) center no-repeat;
}
.roomcounters button:hover .battlecount {
.roomcounters .button:hover .battlecount {
background: url(https://play.pokemonshowdown.com/sprites/gen5ani/meloetta-pirouette.gif) center no-repeat;
}
@ -1429,37 +1432,29 @@ pre.textbox.textbox-empty[placeholder]:before {
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.userlist-count, .userlist .userlist-count {
.userlist-hidden {
width: auto;
border-right: 0;
bottom: auto;
overflow: visible;
}
.userlist-hidden .button.button-middle {
margin: -1px -1px 0 0;
}
.userlist-static {
position: static;
border-right: 0;
}
.userlist-count {
text-align: center;
padding: 2px 0;
}
.userlist-minimized {
height: 21px;
bottom: auto;
overflow: hidden;
background: #EEF2F5;
border-bottom: 1px solid #AAAAAA;
}
.userlist-maximized {
height: auto;
bottom: 0;
overflow: auto;
background: #EEF2F5;
}
.userlist-minimized .userlist-count,
.userlist-maximized .userlist-count {
cursor: pointer;
}
.userlist-minimized .userlist-count:hover,
.userlist-maximized .userlist-count:hover {
background: #DEE4EA;
}
.chat-log.hasuserlist,
.chat-log-add.hasuserlist,
.tournament-wrapper.hasuserlist {
left: 146px;
}
.userlist,
.userlist ul,
.userlist li {
display: block;
list-style-type: none;
@ -1467,8 +1462,10 @@ pre.textbox.textbox-empty[placeholder]:before {
padding: 0;
text-align: left;
}
.userlist-hidden ul {
display: none;
}
.userlist li {
height: 19px;
font: 10pt Verdana, sans-serif;
white-space: nowrap;
}
@ -1491,7 +1488,7 @@ pre.textbox.textbox-empty[placeholder]:before {
border: 0;
padding: 1px 0;
margin: 0;
height: 19px;
height: 21px;
width: 100%;
white-space: nowrap;
font: 9pt Verdana, sans-serif;