I dagens premie opplæring og screencast, Jeg skal vise deg hvordan du lager en slank kalender-widget. Vi bruker CSS3 for å gi det et skinnende utseende, og deretter legge til ganske fin funksjonalitet med JavaScript.
Jeg følger bloggen 365PSD, et veldig pent nettsted som tilbyr en gratis PSD-vanligvis et lite stykke brukergrensesnitt-hver dag. For dag 81 var det virkelig fin kalender widget. Jeg skjønte det ville ikke være for vanskelig å bygge den virkelige tingen, så jeg skal vise deg hvordan du gjør det i dag!
Vi starter med å bygge vår HTML-struktur. Selvfølgelig begynner vi med skjelettet:
Kalender Widget
Så, i kroppen begynner vi med en div
å pakke alt sammen; da har vi tre hovedavsnitt inne i det:
Først har vi det div.header
; ser tilbake på vår PSD, kan vi se at dette tilsvarer toppdelen, delen som holder måneden, månedsbytterne og bindingene. Da har vi et bord for dagens navn. Endelig har vi en div # cal-frame
. Dette er kalendernettet.
Jeg vil gi deg en hemmelighet: Når jeg opprinnelig opprettet denne kalenderen brukergrensesnittet, hadde jeg bare ett bord med en thead
for dagene og a tbody
for kalendernettet; men når jeg begynte å skrive JavaScript for å bytte mellom måneder, ble det tydelig at jeg trengte å bruke noe mer fleksibelt. Du vil se hvorfor når vi kommer til JavaScript.
Så kaste dette opp i den overskriften:
〈 20. juni og 0 〉
Vi har fem elementer her; på utsiden har vi venstre og høyre kalenderbrytere; siden jeg ikke ville bruke noen bilder i dette prosjektet, fant jeg HTML-enhetene 〈 og & rang (⟨og ∧, henholdsvis). Da har vi to tomme spenner for kalenderbindingene. Endelig har vi måned / år etiketten i midten.
Innholdet for Tabellen # dager
er ganske enkelt:
sol man ti ons to fr lør
Til slutt har vi guts av div # cal-frame
; sjekk det ut, og så diskuterer vi det:
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
1. 3 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Så hva har vi her? I utgangspunktet lager vi kalendernettet med et bord (Senere setter vi inn den aktuelle måneden dynamisk). De riktige cellene har datonumrene; hvis cellene er tomme, har de klassen "null"; Til slutt har dagens dato klassen "i dag".
Og egentlig, det er omfanget av HTML; det er ikke mye å se akkurat nå, men her er det vi har:
La oss starte med litt miljø:
kropp bakgrunn: # e0e0e0; #cal -moz-boks-skygge: 0px 3px 3px rgba (0, 0, 0, 0.25); -webkit-boks-skygge: 0px 3px 3px rgba (0, 0, 0, 0.25); margin: 50px auto; font: 13px / 1.5 "Helvetica Neue", Helvatica, Arial, san-serif; display: table;
Ganske åpenbart, eh? Etter å ha satt en bakgrunnsfarge centrerer vi kalenderenheten horisontalt og gir den en bokseskygge. Selvfølgelig setter vi inn skrifttypen. Men hvorfor stiller vi skjermen til bord? Som standard a div
vil vise i blokk, noe som betyr at det vil ta opp hele tilgjengelig bredde; ved å vise den som et bord, vil det ta opp den minste bredden den kan (mens den fortsatt inneholder det er barn), og fortsatt være et blokkelement.
Deretter la vi fokusere på topplinjen:
#cal .header cursor: default; bakgrunn: # cd310d; bakgrunn: -moz-lineær-gradient (topp, # b32b0c, # cd310d); bakgrunn: -webkit-gradient (lineær, venstre topp, venstre bunn, fra (# b32b0c), til (# cd310d)); høyde: 34px; stilling: relativ; farge: #fff; -webkit-grense-topp-venstre-radius: 5px; -webkit-grense-topp-høyre-radius: 5px; -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; Grense øverst til venstre: 5px; Grense-topp-høyre-radius: 5px; font-vekt: bold; tekstskygge: 0px -1px 0 # 87260C; tekst-transformer: store bokstaver; #cal .header span display: inline-block; linje-høyde: 34px;
Her er den første delen av header styling; vi begynner med å sette markøren til en peker; På den måten ser teksten ikke ut til å bli valgt. Deretter setter vi en rød bakgrunnsfarge; Men hvis nettleseren støtter det, bruker vi en bakgrunnsgradient: ikke glem å legge den til både mozilla og webkit! Sett deretter høyden til 34px; Vi stiller stillingen til slektning, fordi barna vil være helt posisjonert; Ved posisjon foreldreelementet relativt, vil barna plasseres helt mot foreldrene, i stedet for kroppen. Sett tekstfargen til hvit, runde øverste venstre og høyre hjørne, og gjør skrifttypen fet. Deretter gir du en liten tekstskygge for å få teksten til å se innrykket. Endelig, forvandle teksten til store bokstaver.
Hvert av elementene i overskriften er a span
; hver av disse vil bli vist som en inline blokk. Gi også en linjehøyde på 34px (høyde på toppteksten).
Disse spannene har også noen spesielle klasser, så la oss se på dem:
#cal .header .hook width: 9px; høyde: 28px; posisjon: absolutt; bunn: 60%; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; bakgrunn: #ececec; bakgrunn: -moz-lineær-gradient (høyre topp, #fff, # 827e7d); bakgrunn: -webkit-gradient (lineær, høyre topp, høyre bunn, fra (#fff) til (# 827e7d)); boks-skygge: 0px -1px 2px rgba (0, 0, 0, 0.65); -moz-boks-skygge: 0px -1px 2px rgba (0, 0, 0, 0,65); -webkit-boks-skygge: 0px -1px 2px rgba (0, 0, 0, 0.65); .right.hook right: 15%; .left.hook left: 15%;
For det første har vi "krok" -klassen; husk, det er kaker eller bindinger i kalenderen. Vi stiller bredden og høyden. Deretter plasserer du det helt. Da flytter vi det opp fra bunnen 60%. Vi kommer rundt hjørnet nok for å få bindingene til å se seg rundt. Da skal vi sette en bakgrunnsfarge; Hvis nettleseren støtter gradienter, overstyrer vi den solide bakgrunnen med en gradient. Da gir vi dem en bokseskygge.
Vi bruker deretter plasseringsklassene til å plassere krokene horisontalt; Hvis elementet har både "krok" og "høyre" klasse, flytt det 15% fra høyre; hvis den har klassen "venstre", flytt den 15% fra venstre.
Nå har vi månedsknappene:
#cal .header. button width: 24px; tekst-Justering: center; stilling: absolutt; #cal .header .left.button left: 0; -webkit-grense-topp-venstre-radius: 5px; -moz-border-radius-topleft: 5px; Grense øverst til venstre: 5px; grense-høyre: 1px solid # ae2a0c; #cal .header .right.button right: 0; topp: 0; border-left: 1px solid # ae2a0c; -webkit-grense-topp-høyre-radius: 5px; -moz-border-radius-topright: 5px; Grense-topp-høyre-radius: 5px; #cal .header. button: svever bakgrunn: -moz-lineær-gradient (topp, # d94215, # bb330f); bakgrunn: -webkit-gradient (lineær, venstre topp, venstre bunn, fra (# d94215), til (# bb330f));
Vi stiller bredden på disse knappene og senterer teksten. selvfølgelig må vi også plassere dem absolutt. Deretter flytter vi til venstre for venstre knapp og rundt øverst til venstre for. For høyre knapp går den til høyre og runder øverste høyre hjørne.
Til slutt legger vi til en svingeffekt for knappene; Selvfølgelig bruker vi en gradient.
Det er enda et element å style: det er månedens etikett.
#cal .header .month-year letter-spacing: 1px; bredde: 100%; tekst-align: center;
Vi bruker avstand mellom bokstavene
å gi tegnene litt mer puste rom. Da gir vi spenningen en bredde på 100% og senterer teksten. Fordi alle søskenelementene er plassert helt, gir dette hele bredden akkurat det vi ønsker.
Så det er hele heisen! Jeg bør nevne at selv om vi har plassert mesteparten av elementene helt, fordi vi bruker prosentandeler til å plassere dem, skaler alt perfekt når du øker eller senker skriftstørrelsen i nettleseren.
OK, la oss gå videre til dagens overskrifter.
#cal tabell bakgrunn: #fff; border-kollaps: kollaps; #cal td color: # 2b2b2b; bredde: 30px; høyde: 30px; linje-høyde: 30px; tekst-Justering: center; grense: 1px solid # e6e6e6; markøren: default; #cal #days td høyde: 26px; linjehøyde: 26px; text-transform: store bokstaver; font-size: 90%; color: # 9e9e9e; #cal #days td: ikke (: siste barn) grense-høyre: 1px solid #fff;
Vi starter med to litt mer generiske selektorer: Dagens topptekst og kalendernettet er begge tabellene, så den første regelen gjelder for begge: Vi setter bakgrunnen til hvit og kolliderer grensene. Når bordgrenser kollapses, har de ingen polstring mellom dem og tilstøtende celler deler grenser. Så, for alle tabellceller, gir vi dem en tekstfarge, sett bredden, høyden og linjens høyde til 30px, og senter teksten. De får alle en grense og standardmarkøren (en pil / peker);
Deretter legger vi til noe spesifikt styling for tabellcellene i dagtabellen: Vi reduserer høyden og linjens høyde litt, sørg for at de er store, og tilbakestill skriftstørrelsen og tekstfargen. ( Merk: I den medfølgende skjermbildet skrev jeg #dag
i stedet for #dager
i selgeren for den tredje blokken ovenfor og aldri korrigert den; sørg for at du får det riktig!)
Hva er den siste regelen ovenfor for? Vel, for tiden, er det lys grå grenser på dagens navn celler. Vi ønsker å endre fargen på grensene til høyre mot hvitt, så de er ikke synlige. Vi vil imidlertid ikke gjøre dette til den siste cellen i raden. Så, vi kan bruke to pseudoklasser. : ikke vil ta en ekskluderingsvelger "parameter." : Siste barn tar det siste barnet av elementene vi allerede har valgt: i dette tilfellet er det tabellcellene. Da setter vi bare den rette grensen til solid hvit.
#cal # cal-frame td.today bakgrunn: #ededed; color: # 8c8c8c; boks-skygge: 1px 1px 0px #fff-innsett; -moz-box-shadow: 1px 1px 0px #fff-innsett; -webkit-boks-skygge: 1px 1px 0px #fff-innsett; #cal # cal-frame td: ikke (.nil): svever farge: #fff; tekstskygge: # 6C1A07 0px -1px; bakgrunn: # CD310D; bakgrunn: -moz-lineær-gradient (topp, # b32b0c, # cd310d); bakgrunn: -webkit-gradient (lineær, venstre topp, venstre bunn, fra (# b32b0c), til (# cd310d)); -moz-box-skygge: 0px 0px 0px; -webkit-boks-skygge: 0px 0px 0px;
Disse to reglene er rettet mot kalendernettet. For tabellcellen med klassen "i dag" setter vi bakgrunnen til en lysegrå og teksten til en mørkere grå. Deretter setter vi en bokseskygge: Det er en hvit skygge, men vi bruker ingen uskarphet, så det er en hvit linje; vi skyver den opp og til den høyre piksel, så vi får en sekundærgrenseeffekt. Legg merke til at vi legger til "innsett" i boksskyggedeklarasjonen, slik at skyggen er inne i cellen.
Den neste regelen gjelder en hover-effekt på alle tabellcellene i kalendernettet, unntatt de med klassen "null"; Vi setter teksten til hvit og legger til en tekstskygge. Så bytter vi bakgrunnen til rød, bruker en gradient når vi kan. Vi inkluderer bokseskyggefjerningen spesielt for "i dag" cellen.
Det er et spesielt tilfelle som vi ikke har nevnt ennå; Ta tak i din nærmeste kalender-nei, ikke iCal, jeg snakker om en ekte dødt papirkalender - og se på, oh, si, oktober 2010. Du vil legge merke til at det er den siste uken med en doblet celle, med begge 24th og den 31st i samme torg. Vi må gjøre det, så la oss stilte for det.
Måten vi merker opp på, er å sette hver dato i et spekter inne i tabellcellen.
#cal # cal-frame td span font-size: 80%; stilling: i forhold; #cal # cal-frame td span: første barn bunn: 5px; #cal # cal-frame td span: siste barn topp: 5px;
Først posisjonerer vi span
s relativt og krympe fonten deres bare et hår; Så flytter vi den første opp 5px og den andre ned 5px.
Vi gjør enda en ting; når vi bytter mellom måneder, ønsker vi å falme fra den ene til den andre; Dette krever at de to bordene ligger på toppen av hverandre. Vi kan oppnå det med dette:
#cal # cal-frame table.curr float: left; #cal # cal-frame table.temp posisjon: absolutt;
Den vi fader ut vil ha en klasse av "temp"; den nye som vi bringer inn for å bo (for en stund) vil ha klassen "curr."
Og det er det for CSS! La oss nå gå videre til noen funksjoner.
Vi gjør funksjonaliteten for kalenderen enkel å gjenbruke; i lys av det begynner vi med dette skjelettet:
Var CALENDAR = funksjon () var, etikett, måneder = ["januar", "februar", "mars", "april", "mai", "juni" , "Oktober", "november", "desember"]; funksjon init (newWrap) funksjon switchMonth (neste, måned, år) funksjon createCal (år, måned) createCal.cache = ; return init: init, switchMonth: switchMonth, createCal: createCal; ;
Så vi lager tre funksjoner i vår KALENDER-funksjon; en vil initialisere kalender-widgeten, den andre vil flytte mellom måneder, og den tredje vil faktisk lage kalendernettet; Legg merke til den linjen etter det: createCal.cache =
; vi skal diskutere det også!
Vi har også opprettet tre variabler på toppen: vi gir pakke inn
og merkelapp
verdier innenfor i det
, men måneder er en matrise med månedens navn.
Her er innholdet i vår i det
funksjon:
wrap = $ (newWrap || "#cal"); label = wrap.find ("# label"); wrap.find ("# prev"). bind ("click.calendar", funksjon () switchMonth (false);); wrap.find ("# next"). bind ("click.calendar", funksjon () switchMonth (true);); label.bind ("klikk", funksjon () switchMonth (null, ny dato (). getMonth (), ny dato (). getFullYear ());); label.click ();
Vi begynner med å gi pakke inn
og merkelapp
De riktige verdiene: Legg merke til at vi bruker väljeren bestått til i det
å finne innpakning, men fall tilbake til "#cal" hvis en ikke er gitt. Deretter binder vi klikkhendelser til neste og forrige kalenderknapper; disse kalles switchMonth
funksjon; hvis vi vil ha den neste kalenderen, passerer vi sant, ellers passerer vi falsk.
derimot, switchMonth
kan også ta to flere parametere; Vi bruker de for klikkhendelsen på etiketten. Når brukeren klikker månedens navn, bytter vi til den aktuelle måneden; Så, vi vil passere i den nåværende måneden og året, som vi kan få fra JavaScript Dato
gjenstand. Ikke glem å sette neste parameter til null!
En ting til og med (og en bonusspiss, det er ikke i skjermbildet!): Når brukeren laster inn siden, vil vi laste inn riktig måned i løpet av måneden som er kodet inn. Den enkleste måten å gjøre det på er å ringe jQuery-klikkmetoden på etiketten uten noen parametere; Dette simulerer et museklikk, og bringer kalenderen til gjeldende måned.
La oss gå videre til switchMonth
funksjon:
var curr = label.text (). trim (). split (""), kalender, tempYear = parseInt (curr [1], 10); måned = måned || 0: months.indexOf (curr [0]) + 1): ((curr [0] === "januar")? 11: months.indexOf (curr [0]) - 1)); år = år || ((neste && måned === 0)? tempYear + 1: (! neste && måned === 11)? tempYear - 1: tempYear);
Vi stiller noen få variabler øverst; Vi splitter etiketten inn i en gruppe som heter curr
; vi lager også en kalender
variabel og griper året av kalenderen som vises.
Så blir det komplisert. Jeg har brukt JavaScript-betingede operatører her, så jeg kan sette alt på en linje. Snarere enn å prøve å forklare det, sjekk dette ut: dette er hva de gjør:
hvis (! måned) hvis (neste) hvis (curr [0] === "desember") måned = 0; ellers month = months.indexOf (curr [0]) + 1; annet hvis (curr [0] === "januar") month = 11; ellers month = months.indexOf (curr [0]) - 1;
Du kan se hvorfor den betingede operatøren er attraktiv: det er bare en linje. Her er den utvidede versjonen av året variabel:
hvis (! år) hvis (neste && måned === 0) år = tempYear + 1; annet hvis (! neste && måned === 11) år = tempYear - 1; ellers år = tempYear;
På slutten av det hele, måned
og år
vil være de riktige verdiene for kalenderen vi prøver å vise brukeren. Hvis du vil føle deg mer komfortabel, kan du erstatte de to linjene med utdragene ovenfor.
Deretter lager vi kalenderen og justerer DOM i henhold til dette:
kalender = createCal (år, måned); $ ("# cal-frame", vikle) .find (". curr") .removeClass ("curr") .addClass ("temp") .end () .prepend (calendar.calendar ()) .find (" .temp ") .fadeOut (" slow ", funksjon () $ (dette) .remove ();); $ ( '# Label') tekst (calendar.label.);
Hva er i kalenderobjektet som returnerer fra createCal
funksjon? Det er et objekt, som dette:
kalender: funksjon () / * returnerer et jquery-objekt i kalenderen * /, etikett: "Månedår"
Vi diskuterer hvorfor kalenderegenskapen er en metode når vi kommer til å bygge den. For nå, la oss gå tilbake til å sette den på siden. Vi får kalenderen og finner den kalenderen som viser kalenderen. Da fjerner vi klassen "curr" og bruker klassen "temp"; så legger vi inn den nye kalenderen (som for øvrig kommer med klassen "curr"), og fader ut og fjerner den gamle.
Vel, vi har bare en funksjon til å gå: createCal
.
var dag = 1, jeg, j, haveDays = true, startDay = nytt Dato (år, måned, dag) .getDay (), daysInMonths = [31, ((år% 4 == 0) && (år% 100! = 0)) || (år% 400 == 0))? 29: 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], kalender = [];
Her er vår start: variablene. Vi har dag
, sett til 1; vi har to vars for iterasjon: Jeg
og j
. Deretter finner vi ut hvilken dag i uken måneden starter; Vi kan gjøre dette ved å opprette et Date-objekt for den første dagen i måneden og ringe getDay
.
Deretter oppretter vi en matrise som holder antall dager i hver måned; For februar må vi regne med springår, så bruk et annet ternært uttrykk for å beregne det.
Til slutt har vi det svært viktige kalender
variabel, som er en matrise.
Deretter vil vi bruke det cache
eiendom vi legger på createCal
funksjon. (Fordi alt i JavaScript er et objekt, kan selv funksjoner ha egenskaper.)
hvis (createCal.cache [år]) if (createCal.cache [år] [måned]) return createCal.cache [år] [måned]; else createCal.cache [year] = ;
Her er det som skjer: det er en mulighet for at brukeren vil "be om" samme måned mer enn en gang. Når vi oppretter det første gang, er det ikke nødvendig å gjøre det igjen; Vi legger det i cachen og trekker det ut senere.
Hvis det cache
objekt har en eiendom med navnet på året vi leter etter, vi kan da sjekke tilgjengeligheten av måneden; Hvis vi allerede har gjort måneden, returnerer vi det bufret objektet. Hvis det ikke er noen eiendom for året, gjør vi det, fordi vi må sette måneden vi skal lage i den.
Hvis vi passerer dette punktet, må vi begynne å lage kalenderen for den forespurte måneden.
i = 0; mens (haveDays) calendar [i] = []; for (j = 0; j < 7; j++) if (i === 0) if (j === startDay) calendar[i][j] = day++; startDay++; else if (day <= daysInMonths[month]) calendar[i][j] = day++; else calendar[i][j] = ""; haveDays = false; if (day > daysInMonths [month]) haveDays = false; i ++;
Dette er en komplisert bit; mens haveDays
variabel er sant, vi vet at vi har dager igjen i måneden. Derfor bruker vi vår Jeg
iterator for å legge til en uke-array i kalenderarrangementet. Deretter bruker vi en for-loop på j
iterator, mens det er mindre enn 7; siden vi begynner med 0, vil dette gi oss 7 dager for ut uke rekkefølge. I vår forløp er det tre saker.
Først må vi sjekke om vi er i den første uken i måneden; hvis vi er, starter vi ikke nødvendigvis på den første dagen. Vi vet allerede hvilken dag måneden begynner på; det er i vår startDay
variabel. Derfor, hvis j === startDay
, Vi er på den rette dagen for å starte, så vi vil sette verdien av dag
i riktig spor. Så øker vi dag
og startDay
av en. Neste gang 'runde for-løkken, j
og startDay
vil være det samme, så det vil fortsette å fungere for resten av uken.
Hvis vi ikke er i den første uken (jeg! == 0
), så sørger vi for at vi fortsatt har dager igjen for å legge til kalenderen; Hvis så, slår vi dem på plass. Til slutt, hvis vi ikke er i den første uken, og vi ikke har dager igjen for å legge til måneden, legger vi inn en tom streng i stedet. Så setter vi inn haveDays
til falsk.
På slutten vil vi sjekke for å se om dag
er større enn antall dager i måneden; Hvis det er, skal vi sette haveDays
til falsk. Dette er for det spesielle tilfellet der måneden slutter på en lørdag.
Selvfølgelig, ikke glem å øke Jeg
like utenfor forløpet!
hvis (kalenderen [5]) for (i = 0; i < calendar[5].length; i++) if (calendar[5][i] !== "") calendar[4][i] = ""+ kalender [4] [i] +""+ kalender [5] [i] +""; kalender = calendar.slice (0, 5);
Vi ønsker ikke at vår kalender skal ha mer enn 5 uker; Hvis en dag eller to spilder inn i uke 6, deler vi cellene i uke 5, som vi har forberedt på i vårt CSS. Så, hvis det er en 6th array i kalender-arrayet, slår vi over det. Hvis innholdet i array-elementet ikke er en tom streng, vil vi deretter tilordne verdien av cellen direkte over "rad" 6: vi vil pakke inn den verdien i et spenning og sammenkoble et annet spekter med riktig verdi av rad 6 innsiden. Det er fornuftig, riktig?
Når vi har alt på plass, kutter vi det siste elementet ut av kalender
.
for (i = 0; i < calendar.length; i++) calendar[i] = ""; kalender = $ (" "+ kalender [i] .join (" ") +"
Nå er det på tide å sammenfatte hver uke i vår kalender
; Vi slår over hverandre i en forløp og setter opp oppføringene i tabellrader, hver dag inne i en tabellcelle. Deretter setter vi hele greia inn i et jQuery-objekt, etter å ha satt alle tabellrader sammen og pakket dem sammen med et bord. Vi legger da klassen "curr" til den tabellen.
Alle de tomme tabellcellene (vi kan bruke jQuery pseudo-selector: tom for å finne dem), få klassen "null".
Hvis vi lager en kalender for den aktuelle måneden, finner vi cellen for i dag og gir den klassen "i dag"; vi kan finne det ved å overføre en funksjon til jQuery-filtermetoden. Funksjonen returnerer sann hvis teksten i cellen stemmer overens med datoen.
Til slutt lager vi vårt ferdige objekt og legger det i hurtigbufferen. Hvorfor lager vi kalender
eiendom en funksjon? Vel, hvis vi nettopp returnerte et jQuery-objekt, en gang vi la det til kalenderen og deretter flyttet videre til en annen måned, ble tabellen fjernet fra DOM; senere, hvis vi kommer tilbake til den måneden, vil elementet ikke vises fordi hurtigbufferen refererer til det samme DOM-elementet. Så vi bruker jQuery's klonemetode for å returnere en kopi av DOM-elementet. Deretter får etiketten månedsnavnet fra månedene og sammenkoblet det med året. Til slutt returnerer vi objektet.
Vi er ferdige! Tilbake i filen index.html, legger vi til en skriptkode med dette:
var cal = KALENDER (); cal.init ();
Det er det! Her ser det ut som vårt ferdige produkt ser ut!
Men jeg kan ikke vise deg funksjonaliteten; du må sjekke koden selv! Takk for at du leste!